From 429e2f632b963c9ee220fe23f9cc53281a426f75 Mon Sep 17 00:00:00 2001 From: Richard Allen Date: Sun, 28 Jun 2009 18:02:02 +0000 Subject: [PATCH] Adding the engine code and the file enginediff documenting changes from regular IOQ3 engines --- reaction/engine/.svnignore | 2 + reaction/engine/BUGS | 4 + reaction/engine/COPYING.txt | 281 + reaction/engine/ChangeLog | 3150 ++++++++ reaction/engine/Makefile | 2176 ++++++ reaction/engine/NOTTODO | 1 + reaction/engine/README | 536 ++ reaction/engine/TODO | 1 + reaction/engine/code/AL/VERSION | 16 + reaction/engine/code/AL/al.h | 506 ++ reaction/engine/code/AL/alc.h | 166 + reaction/engine/code/AL/alctypes.h | 142 + reaction/engine/code/AL/altypes.h | 352 + reaction/engine/code/AL/alut.h | 90 + reaction/engine/code/SDL12/include/SDL.h | 94 + .../engine/code/SDL12/include/SDL_active.h | 58 + .../engine/code/SDL12/include/SDL_audio.h | 253 + .../engine/code/SDL12/include/SDL_byteorder.h | 24 + .../engine/code/SDL12/include/SDL_cdrom.h | 171 + .../engine/code/SDL12/include/SDL_config.h | 45 + .../code/SDL12/include/SDL_config_amiga.h | 80 + .../code/SDL12/include/SDL_config_dreamcast.h | 106 + .../code/SDL12/include/SDL_config_macos.h | 112 + .../code/SDL12/include/SDL_config_macosx.h | 132 + .../code/SDL12/include/SDL_config_minimal.h | 62 + .../code/SDL12/include/SDL_config_os2.h | 141 + .../code/SDL12/include/SDL_config_win32.h | 173 + .../engine/code/SDL12/include/SDL_copying.h | 22 + .../engine/code/SDL12/include/SDL_cpuinfo.h | 75 + .../engine/code/SDL12/include/SDL_endian.h | 192 + .../engine/code/SDL12/include/SDL_error.h | 61 + .../engine/code/SDL12/include/SDL_events.h | 337 + .../engine/code/SDL12/include/SDL_getenv.h | 24 + .../engine/code/SDL12/include/SDL_joystick.h | 167 + .../engine/code/SDL12/include/SDL_keyboard.h | 121 + .../engine/code/SDL12/include/SDL_keysym.h | 311 + .../engine/code/SDL12/include/SDL_loadso.h | 74 + reaction/engine/code/SDL12/include/SDL_main.h | 98 + .../engine/code/SDL12/include/SDL_mouse.h | 136 + .../engine/code/SDL12/include/SDL_mutex.h | 162 + reaction/engine/code/SDL12/include/SDL_name.h | 11 + .../engine/code/SDL12/include/SDL_opengl.h | 6551 +++++++++++++++++ .../engine/code/SDL12/include/SDL_platform.h | 104 + reaction/engine/code/SDL12/include/SDL_quit.h | 50 + .../engine/code/SDL12/include/SDL_rwops.h | 139 + .../engine/code/SDL12/include/SDL_stdinc.h | 586 ++ .../engine/code/SDL12/include/SDL_syswm.h | 210 + .../engine/code/SDL12/include/SDL_thread.h | 119 + .../engine/code/SDL12/include/SDL_timer.h | 115 + .../engine/code/SDL12/include/SDL_types.h | 24 + .../engine/code/SDL12/include/SDL_version.h | 85 + .../engine/code/SDL12/include/SDL_video.h | 889 +++ .../engine/code/SDL12/include/begin_code.h | 150 + .../engine/code/SDL12/include/close_code.h | 41 + reaction/engine/code/asm/ftola.s | 160 + reaction/engine/code/asm/matha.s | 424 ++ reaction/engine/code/asm/qasm.h | 46 + reaction/engine/code/asm/snapvectora.s | 103 + reaction/engine/code/asm/snd_mixa.s | 217 + reaction/engine/code/botlib/aasfile.h | 267 + reaction/engine/code/botlib/be_aas.h | 221 + reaction/engine/code/botlib/be_aas_bsp.h | 89 + reaction/engine/code/botlib/be_aas_bspq3.c | 487 ++ reaction/engine/code/botlib/be_aas_cluster.c | 1545 ++++ reaction/engine/code/botlib/be_aas_cluster.h | 38 + reaction/engine/code/botlib/be_aas_debug.c | 777 ++ reaction/engine/code/botlib/be_aas_debug.h | 62 + reaction/engine/code/botlib/be_aas_def.h | 306 + reaction/engine/code/botlib/be_aas_entity.c | 437 ++ reaction/engine/code/botlib/be_aas_entity.h | 63 + reaction/engine/code/botlib/be_aas_file.c | 582 ++ reaction/engine/code/botlib/be_aas_file.h | 42 + reaction/engine/code/botlib/be_aas_funcs.h | 47 + reaction/engine/code/botlib/be_aas_main.c | 429 ++ reaction/engine/code/botlib/be_aas_main.h | 61 + reaction/engine/code/botlib/be_aas_move.c | 1101 +++ reaction/engine/code/botlib/be_aas_move.h | 71 + reaction/engine/code/botlib/be_aas_optimize.c | 312 + reaction/engine/code/botlib/be_aas_optimize.h | 33 + reaction/engine/code/botlib/be_aas_reach.c | 4538 ++++++++++++ reaction/engine/code/botlib/be_aas_reach.h | 68 + reaction/engine/code/botlib/be_aas_route.c | 2210 ++++++ reaction/engine/code/botlib/be_aas_route.h | 67 + reaction/engine/code/botlib/be_aas_routealt.c | 240 + reaction/engine/code/botlib/be_aas_routealt.h | 40 + reaction/engine/code/botlib/be_aas_sample.c | 1393 ++++ reaction/engine/code/botlib/be_aas_sample.h | 69 + reaction/engine/code/botlib/be_ai_char.c | 790 ++ reaction/engine/code/botlib/be_ai_char.h | 48 + reaction/engine/code/botlib/be_ai_chat.c | 3042 ++++++++ reaction/engine/code/botlib/be_ai_chat.h | 113 + reaction/engine/code/botlib/be_ai_gen.c | 134 + reaction/engine/code/botlib/be_ai_gen.h | 33 + reaction/engine/code/botlib/be_ai_goal.c | 1821 +++++ reaction/engine/code/botlib/be_ai_goal.h | 118 + reaction/engine/code/botlib/be_ai_move.c | 3570 +++++++++ reaction/engine/code/botlib/be_ai_move.h | 142 + reaction/engine/code/botlib/be_ai_weap.c | 543 ++ reaction/engine/code/botlib/be_ai_weap.h | 104 + reaction/engine/code/botlib/be_ai_weight.c | 918 +++ reaction/engine/code/botlib/be_ai_weight.h | 83 + reaction/engine/code/botlib/be_ea.c | 508 ++ reaction/engine/code/botlib/be_ea.h | 66 + reaction/engine/code/botlib/be_interface.c | 895 +++ reaction/engine/code/botlib/be_interface.h | 57 + reaction/engine/code/botlib/botlib.h | 516 ++ reaction/engine/code/botlib/l_crc.c | 151 + reaction/engine/code/botlib/l_crc.h | 29 + reaction/engine/code/botlib/l_libvar.c | 295 + reaction/engine/code/botlib/l_libvar.h | 63 + reaction/engine/code/botlib/l_log.c | 169 + reaction/engine/code/botlib/l_log.h | 46 + reaction/engine/code/botlib/l_memory.c | 463 ++ reaction/engine/code/botlib/l_memory.h | 76 + reaction/engine/code/botlib/l_precomp.c | 3230 ++++++++ reaction/engine/code/botlib/l_precomp.h | 180 + reaction/engine/code/botlib/l_script.c | 1431 ++++ reaction/engine/code/botlib/l_script.h | 247 + reaction/engine/code/botlib/l_struct.c | 462 ++ reaction/engine/code/botlib/l_struct.h | 75 + reaction/engine/code/botlib/l_utils.h | 37 + reaction/engine/code/botlib/lcc.mak | 55 + reaction/engine/code/botlib/linux-i386.mak | 92 + reaction/engine/code/cgame/cg_consolecmds.c | 578 ++ reaction/engine/code/cgame/cg_draw.c | 2663 +++++++ reaction/engine/code/cgame/cg_drawtools.c | 817 ++ reaction/engine/code/cgame/cg_effects.c | 718 ++ reaction/engine/code/cgame/cg_ents.c | 1037 +++ reaction/engine/code/cgame/cg_event.c | 1205 +++ reaction/engine/code/cgame/cg_info.c | 297 + reaction/engine/code/cgame/cg_local.h | 1668 +++++ reaction/engine/code/cgame/cg_localents.c | 886 +++ reaction/engine/code/cgame/cg_main.c | 1988 +++++ reaction/engine/code/cgame/cg_marks.c | 2274 ++++++ reaction/engine/code/cgame/cg_newdraw.c | 1849 +++++ reaction/engine/code/cgame/cg_particles.c | 2018 +++++ reaction/engine/code/cgame/cg_players.c | 2618 +++++++ reaction/engine/code/cgame/cg_playerstate.c | 526 ++ reaction/engine/code/cgame/cg_predict.c | 628 ++ reaction/engine/code/cgame/cg_public.h | 238 + reaction/engine/code/cgame/cg_scoreboard.c | 534 ++ reaction/engine/code/cgame/cg_servercmds.c | 1121 +++ reaction/engine/code/cgame/cg_snapshot.c | 403 + reaction/engine/code/cgame/cg_syscalls.asm | 106 + reaction/engine/code/cgame/cg_syscalls.c | 445 ++ reaction/engine/code/cgame/cg_view.c | 876 +++ reaction/engine/code/cgame/cg_weapons.c | 2277 ++++++ reaction/engine/code/client/cl_avi.c | 671 ++ reaction/engine/code/client/cl_cgame.c | 1103 +++ reaction/engine/code/client/cl_cin.c | 1668 +++++ reaction/engine/code/client/cl_console.c | 795 ++ reaction/engine/code/client/cl_curl.c | 339 + reaction/engine/code/client/cl_curl.h | 102 + reaction/engine/code/client/cl_input.c | 1009 +++ reaction/engine/code/client/cl_keys.c | 1548 ++++ reaction/engine/code/client/cl_main.c | 4197 +++++++++++ reaction/engine/code/client/cl_net_chan.c | 167 + reaction/engine/code/client/cl_parse.c | 911 +++ reaction/engine/code/client/cl_scrn.c | 595 ++ reaction/engine/code/client/cl_ui.c | 1154 +++ reaction/engine/code/client/client.h | 625 ++ reaction/engine/code/client/keycodes.h | 279 + reaction/engine/code/client/keys.h | 55 + reaction/engine/code/client/libmumblelink.c | 134 + reaction/engine/code/client/libmumblelink.h | 26 + reaction/engine/code/client/qal.c | 351 + reaction/engine/code/client/qal.h | 245 + reaction/engine/code/client/snd_adpcm.c | 330 + reaction/engine/code/client/snd_codec.c | 237 + reaction/engine/code/client/snd_codec.h | 98 + reaction/engine/code/client/snd_codec_ogg.c | 477 ++ reaction/engine/code/client/snd_codec_wav.c | 294 + reaction/engine/code/client/snd_dma.c | 1541 ++++ reaction/engine/code/client/snd_local.h | 250 + reaction/engine/code/client/snd_main.c | 516 ++ reaction/engine/code/client/snd_mem.c | 265 + reaction/engine/code/client/snd_mix.c | 738 ++ reaction/engine/code/client/snd_openal.c | 2152 ++++++ reaction/engine/code/client/snd_public.h | 82 + reaction/engine/code/client/snd_wavelet.c | 253 + reaction/engine/code/game/ai_chat.c | 1226 +++ reaction/engine/code/game/ai_chat.h | 61 + reaction/engine/code/game/ai_cmd.c | 1992 +++++ reaction/engine/code/game/ai_cmd.h | 37 + reaction/engine/code/game/ai_dmnet.c | 2610 +++++++ reaction/engine/code/game/ai_dmnet.h | 61 + reaction/engine/code/game/ai_dmq3.c | 5460 ++++++++++++++ reaction/engine/code/game/ai_dmq3.h | 206 + reaction/engine/code/game/ai_main.c | 1698 +++++ reaction/engine/code/game/ai_main.h | 299 + reaction/engine/code/game/ai_team.c | 2080 ++++++ reaction/engine/code/game/ai_team.h | 39 + reaction/engine/code/game/ai_vcmd.c | 550 ++ reaction/engine/code/game/ai_vcmd.h | 36 + reaction/engine/code/game/bg_lib.c | 1793 +++++ reaction/engine/code/game/bg_lib.h | 123 + reaction/engine/code/game/bg_local.h | 83 + reaction/engine/code/game/bg_misc.c | 1604 ++++ reaction/engine/code/game/bg_pmove.c | 2069 ++++++ reaction/engine/code/game/bg_public.h | 738 ++ reaction/engine/code/game/bg_slidemove.c | 325 + reaction/engine/code/game/chars.h | 134 + reaction/engine/code/game/g_active.c | 1191 +++ reaction/engine/code/game/g_arenas.c | 376 + reaction/engine/code/game/g_bot.c | 1013 +++ reaction/engine/code/game/g_client.c | 1358 ++++ reaction/engine/code/game/g_cmds.c | 1685 +++++ reaction/engine/code/game/g_combat.c | 1196 +++ reaction/engine/code/game/g_items.c | 1010 +++ reaction/engine/code/game/g_local.h | 972 +++ reaction/engine/code/game/g_main.c | 1845 +++++ reaction/engine/code/game/g_mem.c | 61 + reaction/engine/code/game/g_misc.c | 482 ++ reaction/engine/code/game/g_missile.c | 808 ++ reaction/engine/code/game/g_mover.c | 1612 ++++ reaction/engine/code/game/g_public.h | 429 ++ reaction/engine/code/game/g_rankings.c | 1135 +++ reaction/engine/code/game/g_rankings.h | 396 + reaction/engine/code/game/g_session.c | 190 + reaction/engine/code/game/g_spawn.c | 643 ++ reaction/engine/code/game/g_svcmds.c | 508 ++ reaction/engine/code/game/g_syscalls.asm | 225 + reaction/engine/code/game/g_syscalls.c | 790 ++ reaction/engine/code/game/g_target.c | 467 ++ reaction/engine/code/game/g_team.c | 1484 ++++ reaction/engine/code/game/g_team.h | 88 + reaction/engine/code/game/g_trigger.c | 465 ++ reaction/engine/code/game/g_utils.c | 666 ++ reaction/engine/code/game/g_weapon.c | 1145 +++ reaction/engine/code/game/inv.h | 166 + reaction/engine/code/game/match.h | 134 + reaction/engine/code/game/syn.h | 34 + reaction/engine/code/jpeg-6b/README | 385 + .../engine/code/jpeg-6b/ioq3-changes.diff | 916 +++ reaction/engine/code/jpeg-6b/jcapimin.c | 280 + reaction/engine/code/jpeg-6b/jcapistd.c | 161 + reaction/engine/code/jpeg-6b/jccoefct.c | 449 ++ reaction/engine/code/jpeg-6b/jccolor.c | 459 ++ reaction/engine/code/jpeg-6b/jcdctmgr.c | 389 + reaction/engine/code/jpeg-6b/jchuff.c | 909 +++ reaction/engine/code/jpeg-6b/jchuff.h | 47 + reaction/engine/code/jpeg-6b/jcinit.c | 72 + reaction/engine/code/jpeg-6b/jcmainct.c | 293 + reaction/engine/code/jpeg-6b/jcmarker.c | 664 ++ reaction/engine/code/jpeg-6b/jcmaster.c | 590 ++ reaction/engine/code/jpeg-6b/jcomapi.c | 106 + reaction/engine/code/jpeg-6b/jconfig.h | 40 + reaction/engine/code/jpeg-6b/jcparam.c | 610 ++ reaction/engine/code/jpeg-6b/jcphuff.c | 833 +++ reaction/engine/code/jpeg-6b/jcprepct.c | 354 + reaction/engine/code/jpeg-6b/jcsample.c | 519 ++ reaction/engine/code/jpeg-6b/jctrans.c | 388 + reaction/engine/code/jpeg-6b/jdapimin.c | 395 + reaction/engine/code/jpeg-6b/jdapistd.c | 275 + reaction/engine/code/jpeg-6b/jdatadst.c | 151 + reaction/engine/code/jpeg-6b/jdatasrc.c | 218 + reaction/engine/code/jpeg-6b/jdcoefct.c | 736 ++ reaction/engine/code/jpeg-6b/jdcolor.c | 396 + reaction/engine/code/jpeg-6b/jdct.h | 176 + reaction/engine/code/jpeg-6b/jddctmgr.c | 269 + reaction/engine/code/jpeg-6b/jdhuff.c | 651 ++ reaction/engine/code/jpeg-6b/jdhuff.h | 201 + reaction/engine/code/jpeg-6b/jdinput.c | 381 + reaction/engine/code/jpeg-6b/jdmainct.c | 512 ++ reaction/engine/code/jpeg-6b/jdmarker.c | 1360 ++++ reaction/engine/code/jpeg-6b/jdmaster.c | 557 ++ reaction/engine/code/jpeg-6b/jdmerge.c | 400 + reaction/engine/code/jpeg-6b/jdphuff.c | 668 ++ reaction/engine/code/jpeg-6b/jdpostct.c | 290 + reaction/engine/code/jpeg-6b/jdsample.c | 478 ++ reaction/engine/code/jpeg-6b/jdtrans.c | 143 + reaction/engine/code/jpeg-6b/jerror.c | 256 + reaction/engine/code/jpeg-6b/jerror.h | 291 + reaction/engine/code/jpeg-6b/jfdctflt.c | 168 + reaction/engine/code/jpeg-6b/jfdctfst.c | 224 + reaction/engine/code/jpeg-6b/jfdctint.c | 283 + reaction/engine/code/jpeg-6b/jidctflt.c | 242 + reaction/engine/code/jpeg-6b/jidctfst.c | 368 + reaction/engine/code/jpeg-6b/jidctint.c | 389 + reaction/engine/code/jpeg-6b/jidctred.c | 398 + reaction/engine/code/jpeg-6b/jinclude.h | 116 + reaction/engine/code/jpeg-6b/jload.c | 145 + reaction/engine/code/jpeg-6b/jmemansi.c | 167 + reaction/engine/code/jpeg-6b/jmemdos.c | 638 ++ reaction/engine/code/jpeg-6b/jmemmgr.c | 1118 +++ reaction/engine/code/jpeg-6b/jmemname.c | 276 + reaction/engine/code/jpeg-6b/jmemnobs.c | 105 + reaction/engine/code/jpeg-6b/jmemsys.h | 198 + reaction/engine/code/jpeg-6b/jmorecfg.h | 364 + reaction/engine/code/jpeg-6b/jpegint.h | 392 + reaction/engine/code/jpeg-6b/jpeglib.h | 1096 +++ reaction/engine/code/jpeg-6b/jpegtran.c | 504 ++ reaction/engine/code/jpeg-6b/jquant1.c | 856 +++ reaction/engine/code/jpeg-6b/jquant2.c | 1310 ++++ reaction/engine/code/jpeg-6b/jutils.c | 179 + reaction/engine/code/jpeg-6b/jversion.h | 14 + reaction/engine/code/libcurl/curl/curl.h | 1563 ++++ reaction/engine/code/libcurl/curl/curlver.h | 56 + reaction/engine/code/libcurl/curl/easy.h | 81 + reaction/engine/code/libcurl/curl/mprintf.h | 62 + reaction/engine/code/libcurl/curl/multi.h | 344 + .../engine/code/libcurl/curl/stdcheaders.h | 34 + reaction/engine/code/libcurl/curl/types.h | 1 + .../code/libs/macosx/libSDL-1.2.0.dylib | Bin 0 -> 4474044 bytes .../engine/code/libspeex/_kiss_fft_guts.h | 160 + reaction/engine/code/libspeex/arch.h | 239 + reaction/engine/code/libspeex/bits.c | 372 + reaction/engine/code/libspeex/buffer.c | 176 + reaction/engine/code/libspeex/cb_search.c | 612 ++ reaction/engine/code/libspeex/cb_search.h | 103 + .../engine/code/libspeex/cb_search_arm4.h | 137 + .../engine/code/libspeex/cb_search_bfin.h | 112 + reaction/engine/code/libspeex/cb_search_sse.h | 84 + reaction/engine/code/libspeex/config.h | 20 + .../engine/code/libspeex/echo_diagnostic.m | 72 + .../engine/code/libspeex/exc_10_16_table.c | 50 + .../engine/code/libspeex/exc_10_32_table.c | 66 + .../engine/code/libspeex/exc_20_32_table.c | 66 + .../engine/code/libspeex/exc_5_256_table.c | 290 + .../engine/code/libspeex/exc_5_64_table.c | 98 + .../engine/code/libspeex/exc_8_128_table.c | 162 + reaction/engine/code/libspeex/fftwrap.c | 288 + reaction/engine/code/libspeex/fftwrap.h | 58 + reaction/engine/code/libspeex/filterbank.c | 227 + reaction/engine/code/libspeex/filterbank.h | 66 + reaction/engine/code/libspeex/filters.c | 821 +++ reaction/engine/code/libspeex/filters.h | 90 + reaction/engine/code/libspeex/filters_arm4.h | 96 + reaction/engine/code/libspeex/filters_bfin.h | 515 ++ reaction/engine/code/libspeex/filters_sse.h | 336 + reaction/engine/code/libspeex/fixed_arm4.h | 148 + reaction/engine/code/libspeex/fixed_arm5e.h | 178 + reaction/engine/code/libspeex/fixed_bfin.h | 173 + reaction/engine/code/libspeex/fixed_debug.h | 487 ++ reaction/engine/code/libspeex/fixed_generic.h | 106 + reaction/engine/code/libspeex/gain_table.c | 160 + .../engine/code/libspeex/gain_table_lbr.c | 64 + .../engine/code/libspeex/hexc_10_32_table.c | 66 + reaction/engine/code/libspeex/hexc_table.c | 162 + .../engine/code/libspeex/high_lsp_tables.c | 163 + .../code/libspeex/include/speex/speex.h | 424 ++ .../code/libspeex/include/speex/speex_bits.h | 174 + .../libspeex/include/speex/speex_buffer.h | 68 + .../libspeex/include/speex/speex_callbacks.h | 134 + .../include/speex/speex_config_types.h | 18 + .../code/libspeex/include/speex/speex_echo.h | 123 + .../libspeex/include/speex/speex_header.h | 94 + .../libspeex/include/speex/speex_jitter.h | 197 + .../libspeex/include/speex/speex_preprocess.h | 190 + .../libspeex/include/speex/speex_resampler.h | 340 + .../libspeex/include/speex/speex_stereo.h | 91 + .../code/libspeex/include/speex/speex_types.h | 126 + reaction/engine/code/libspeex/jitter.c | 840 +++ reaction/engine/code/libspeex/kiss_fft.c | 523 ++ reaction/engine/code/libspeex/kiss_fft.h | 108 + reaction/engine/code/libspeex/kiss_fftr.c | 297 + reaction/engine/code/libspeex/kiss_fftr.h | 51 + reaction/engine/code/libspeex/lpc.c | 201 + reaction/engine/code/libspeex/lpc.h | 53 + reaction/engine/code/libspeex/lpc_bfin.h | 131 + reaction/engine/code/libspeex/lsp.c | 656 ++ reaction/engine/code/libspeex/lsp.h | 64 + reaction/engine/code/libspeex/lsp_bfin.h | 89 + reaction/engine/code/libspeex/lsp_tables_nb.c | 360 + reaction/engine/code/libspeex/ltp.c | 839 +++ reaction/engine/code/libspeex/ltp.h | 141 + reaction/engine/code/libspeex/ltp_arm4.h | 187 + reaction/engine/code/libspeex/ltp_bfin.h | 419 ++ reaction/engine/code/libspeex/ltp_sse.h | 92 + reaction/engine/code/libspeex/math_approx.h | 332 + reaction/engine/code/libspeex/mdf.c | 1177 +++ reaction/engine/code/libspeex/misc_bfin.h | 54 + reaction/engine/code/libspeex/modes.c | 362 + reaction/engine/code/libspeex/modes.h | 161 + reaction/engine/code/libspeex/modes_wb.c | 300 + reaction/engine/code/libspeex/nb_celp.c | 1903 +++++ reaction/engine/code/libspeex/nb_celp.h | 203 + reaction/engine/code/libspeex/os_support.h | 169 + reaction/engine/code/libspeex/preprocess.c | 1185 +++ reaction/engine/code/libspeex/pseudofloat.h | 379 + reaction/engine/code/libspeex/quant_lsp.c | 385 + reaction/engine/code/libspeex/quant_lsp.h | 74 + .../engine/code/libspeex/quant_lsp_bfin.h | 165 + reaction/engine/code/libspeex/resample.c | 1179 +++ reaction/engine/code/libspeex/sb_celp.c | 1488 ++++ reaction/engine/code/libspeex/sb_celp.h | 155 + reaction/engine/code/libspeex/smallft.c | 1261 ++++ reaction/engine/code/libspeex/smallft.h | 46 + reaction/engine/code/libspeex/speex.c | 250 + .../engine/code/libspeex/speex_callbacks.c | 144 + reaction/engine/code/libspeex/speex_header.c | 188 + reaction/engine/code/libspeex/stack_alloc.h | 115 + reaction/engine/code/libspeex/stereo.c | 296 + reaction/engine/code/libspeex/testdenoise.c | 44 + reaction/engine/code/libspeex/testecho.c | 53 + reaction/engine/code/libspeex/testenc.c | 146 + reaction/engine/code/libspeex/testenc_uwb.c | 137 + reaction/engine/code/libspeex/testenc_wb.c | 140 + reaction/engine/code/libspeex/vbr.c | 275 + reaction/engine/code/libspeex/vbr.h | 70 + reaction/engine/code/libspeex/vorbis_psy.h | 97 + reaction/engine/code/libspeex/vq.c | 147 + reaction/engine/code/libspeex/vq.h | 54 + reaction/engine/code/libspeex/vq_arm4.h | 115 + reaction/engine/code/libspeex/vq_bfin.h | 107 + reaction/engine/code/libspeex/vq_sse.h | 120 + reaction/engine/code/libspeex/window.c | 102 + reaction/engine/code/null/mac_net.c | 65 + reaction/engine/code/null/null_client.c | 90 + reaction/engine/code/null/null_glimp.c | 56 + reaction/engine/code/null/null_input.c | 37 + reaction/engine/code/null/null_main.c | 116 + reaction/engine/code/null/null_net.c | 64 + reaction/engine/code/null/null_snddma.c | 60 + reaction/engine/code/q3_ui/ui_addbots.c | 412 ++ reaction/engine/code/q3_ui/ui_atoms.c | 1261 ++++ reaction/engine/code/q3_ui/ui_cdkey.c | 291 + reaction/engine/code/q3_ui/ui_cinematics.c | 350 + reaction/engine/code/q3_ui/ui_confirm.c | 293 + reaction/engine/code/q3_ui/ui_connect.c | 266 + reaction/engine/code/q3_ui/ui_controls2.c | 1664 +++++ reaction/engine/code/q3_ui/ui_credits.c | 175 + reaction/engine/code/q3_ui/ui_demo2.c | 291 + reaction/engine/code/q3_ui/ui_display.c | 265 + reaction/engine/code/q3_ui/ui_gameinfo.c | 815 ++ reaction/engine/code/q3_ui/ui_ingame.c | 349 + reaction/engine/code/q3_ui/ui_loadconfig.c | 274 + reaction/engine/code/q3_ui/ui_local.h | 802 ++ reaction/engine/code/q3_ui/ui_login.c | 208 + reaction/engine/code/q3_ui/ui_main.c | 249 + reaction/engine/code/q3_ui/ui_menu.c | 419 ++ reaction/engine/code/q3_ui/ui_mfield.c | 439 ++ reaction/engine/code/q3_ui/ui_mods.c | 247 + reaction/engine/code/q3_ui/ui_network.c | 281 + reaction/engine/code/q3_ui/ui_options.c | 229 + reaction/engine/code/q3_ui/ui_playermodel.c | 731 ++ reaction/engine/code/q3_ui/ui_players.c | 1249 ++++ .../engine/code/q3_ui/ui_playersettings.c | 513 ++ reaction/engine/code/q3_ui/ui_preferences.c | 419 ++ reaction/engine/code/q3_ui/ui_qmenu.c | 1745 +++++ reaction/engine/code/q3_ui/ui_rankings.c | 420 ++ reaction/engine/code/q3_ui/ui_rankstatus.c | 209 + reaction/engine/code/q3_ui/ui_removebots.c | 342 + reaction/engine/code/q3_ui/ui_saveconfig.c | 212 + reaction/engine/code/q3_ui/ui_serverinfo.c | 273 + reaction/engine/code/q3_ui/ui_servers2.c | 1643 +++++ reaction/engine/code/q3_ui/ui_setup.c | 327 + reaction/engine/code/q3_ui/ui_signup.c | 286 + reaction/engine/code/q3_ui/ui_sound.c | 316 + reaction/engine/code/q3_ui/ui_sparena.c | 50 + reaction/engine/code/q3_ui/ui_specifyleague.c | 333 + reaction/engine/code/q3_ui/ui_specifyserver.c | 213 + reaction/engine/code/q3_ui/ui_splevel.c | 1008 +++ reaction/engine/code/q3_ui/ui_sppostgame.c | 644 ++ reaction/engine/code/q3_ui/ui_spreset.c | 194 + reaction/engine/code/q3_ui/ui_spskill.c | 329 + reaction/engine/code/q3_ui/ui_startserver.c | 1968 +++++ reaction/engine/code/q3_ui/ui_team.c | 200 + reaction/engine/code/q3_ui/ui_teamorders.c | 447 ++ reaction/engine/code/q3_ui/ui_video.c | 1181 +++ reaction/engine/code/qcommon/cm_load.c | 842 +++ reaction/engine/code/qcommon/cm_local.h | 196 + reaction/engine/code/qcommon/cm_patch.c | 1777 +++++ reaction/engine/code/qcommon/cm_patch.h | 103 + reaction/engine/code/qcommon/cm_polylib.c | 737 ++ reaction/engine/code/qcommon/cm_polylib.h | 68 + reaction/engine/code/qcommon/cm_public.h | 76 + reaction/engine/code/qcommon/cm_test.c | 521 ++ reaction/engine/code/qcommon/cm_trace.c | 1464 ++++ reaction/engine/code/qcommon/cmd.c | 798 ++ reaction/engine/code/qcommon/common.c | 3346 +++++++++ reaction/engine/code/qcommon/cvar.c | 1110 +++ reaction/engine/code/qcommon/files.c | 3505 +++++++++ reaction/engine/code/qcommon/huffman.c | 447 ++ reaction/engine/code/qcommon/md4.c | 208 + reaction/engine/code/qcommon/md5.c | 310 + reaction/engine/code/qcommon/msg.c | 1771 +++++ reaction/engine/code/qcommon/net_chan.c | 740 ++ reaction/engine/code/qcommon/net_ip.c | 1678 +++++ reaction/engine/code/qcommon/puff.c | 758 ++ reaction/engine/code/qcommon/puff.h | 43 + reaction/engine/code/qcommon/q_math.c | 1309 ++++ reaction/engine/code/qcommon/q_platform.h | 345 + reaction/engine/code/qcommon/q_shared.c | 1393 ++++ reaction/engine/code/qcommon/q_shared.h | 1285 ++++ reaction/engine/code/qcommon/qcommon.h | 1158 +++ reaction/engine/code/qcommon/qfiles.h | 581 ++ reaction/engine/code/qcommon/surfaceflags.h | 80 + reaction/engine/code/qcommon/unzip.c | 4300 +++++++++++ reaction/engine/code/qcommon/unzip.h | 336 + reaction/engine/code/qcommon/vm.c | 916 +++ reaction/engine/code/qcommon/vm_interpreted.c | 913 +++ reaction/engine/code/qcommon/vm_local.h | 183 + reaction/engine/code/qcommon/vm_none.c | 10 + reaction/engine/code/qcommon/vm_powerpc.c | 2170 ++++++ reaction/engine/code/qcommon/vm_powerpc_asm.c | 1009 +++ reaction/engine/code/qcommon/vm_powerpc_asm.h | 156 + reaction/engine/code/qcommon/vm_ppc.c | 2064 ++++++ reaction/engine/code/qcommon/vm_sparc.c | 1648 +++++ reaction/engine/code/qcommon/vm_sparc.h | 78 + reaction/engine/code/qcommon/vm_x86.c | 1220 +++ reaction/engine/code/qcommon/vm_x86_64.c | 1136 +++ .../engine/code/qcommon/vm_x86_64_assembler.c | 1419 ++++ reaction/engine/code/renderer/qgl.h | 380 + reaction/engine/code/renderer/tr_animation.c | 658 ++ reaction/engine/code/renderer/tr_backend.c | 1209 +++ reaction/engine/code/renderer/tr_bsp.c | 1871 +++++ reaction/engine/code/renderer/tr_cmds.c | 565 ++ reaction/engine/code/renderer/tr_curve.c | 626 ++ reaction/engine/code/renderer/tr_flares.c | 529 ++ reaction/engine/code/renderer/tr_font.c | 549 ++ reaction/engine/code/renderer/tr_image.c | 1585 ++++ reaction/engine/code/renderer/tr_image_bmp.c | 239 + reaction/engine/code/renderer/tr_image_jpg.c | 497 ++ reaction/engine/code/renderer/tr_image_pcx.c | 175 + reaction/engine/code/renderer/tr_image_png.c | 2488 +++++++ reaction/engine/code/renderer/tr_image_tga.c | 320 + reaction/engine/code/renderer/tr_init.c | 1243 ++++ reaction/engine/code/renderer/tr_light.c | 394 + reaction/engine/code/renderer/tr_local.h | 1713 +++++ reaction/engine/code/renderer/tr_main.c | 1402 ++++ reaction/engine/code/renderer/tr_marks.c | 443 ++ reaction/engine/code/renderer/tr_mesh.c | 418 ++ reaction/engine/code/renderer/tr_model.c | 1148 +++ reaction/engine/code/renderer/tr_noise.c | 93 + reaction/engine/code/renderer/tr_public.h | 171 + reaction/engine/code/renderer/tr_scene.c | 410 ++ reaction/engine/code/renderer/tr_shade.c | 1495 ++++ reaction/engine/code/renderer/tr_shade_calc.c | 1231 ++++ reaction/engine/code/renderer/tr_shader.c | 3075 ++++++++ reaction/engine/code/renderer/tr_shadows.c | 343 + reaction/engine/code/renderer/tr_sky.c | 845 +++ reaction/engine/code/renderer/tr_surface.c | 1248 ++++ reaction/engine/code/renderer/tr_types.h | 212 + reaction/engine/code/renderer/tr_world.c | 668 ++ reaction/engine/code/sdl/sdl_gamma.c | 91 + reaction/engine/code/sdl/sdl_glimp.c | 1010 +++ reaction/engine/code/sdl/sdl_icon.h | 132 + reaction/engine/code/sdl/sdl_input.c | 1014 +++ reaction/engine/code/sdl/sdl_snd.c | 299 + reaction/engine/code/server/server.h | 460 ++ reaction/engine/code/server/sv_bot.c | 631 ++ reaction/engine/code/server/sv_ccmds.c | 1310 ++++ reaction/engine/code/server/sv_client.c | 1892 +++++ reaction/engine/code/server/sv_game.c | 968 +++ reaction/engine/code/server/sv_init.c | 773 ++ reaction/engine/code/server/sv_main.c | 934 +++ reaction/engine/code/server/sv_net_chan.c | 209 + reaction/engine/code/server/sv_rankings.c | 1537 ++++ reaction/engine/code/server/sv_snapshot.c | 702 ++ reaction/engine/code/server/sv_world.c | 691 ++ reaction/engine/code/sys/con_log.c | 129 + reaction/engine/code/sys/con_passive.c | 68 + reaction/engine/code/sys/con_tty.c | 461 ++ reaction/engine/code/sys/con_win32.c | 358 + reaction/engine/code/sys/sys_loadlib.h | 49 + reaction/engine/code/sys/sys_local.h | 55 + reaction/engine/code/sys/sys_main.c | 574 ++ reaction/engine/code/sys/sys_unix.c | 545 ++ reaction/engine/code/sys/sys_win32.c | 635 ++ reaction/engine/code/sys/win_resource.h | 44 + reaction/engine/code/sys/win_resource.rc | 76 + reaction/engine/code/tools/asm/README.Id | 10 + reaction/engine/code/tools/asm/cmdlib.c | 1138 +++ reaction/engine/code/tools/asm/cmdlib.h | 152 + reaction/engine/code/tools/asm/lib.txt | 31 + reaction/engine/code/tools/asm/mathlib.h | 94 + reaction/engine/code/tools/asm/notes.txt | 16 + reaction/engine/code/tools/asm/ops.txt | 132 + reaction/engine/code/tools/asm/opstrings.h | 175 + reaction/engine/code/tools/asm/q3asm.c | 1646 +++++ reaction/engine/code/tools/lcc/COPYRIGHT | 61 + reaction/engine/code/tools/lcc/LOG | 91 + reaction/engine/code/tools/lcc/README | 21 + reaction/engine/code/tools/lcc/README.id | 3 + reaction/engine/code/tools/lcc/cpp/cpp.c | 327 + reaction/engine/code/tools/lcc/cpp/cpp.h | 166 + reaction/engine/code/tools/lcc/cpp/eval.c | 524 ++ reaction/engine/code/tools/lcc/cpp/getopt.c | 53 + reaction/engine/code/tools/lcc/cpp/hideset.c | 112 + reaction/engine/code/tools/lcc/cpp/include.c | 154 + reaction/engine/code/tools/lcc/cpp/lex.c | 580 ++ reaction/engine/code/tools/lcc/cpp/macro.c | 515 ++ reaction/engine/code/tools/lcc/cpp/nlist.c | 104 + reaction/engine/code/tools/lcc/cpp/tokens.c | 370 + reaction/engine/code/tools/lcc/cpp/unix.c | 128 + reaction/engine/code/tools/lcc/doc/4.html | 754 ++ reaction/engine/code/tools/lcc/doc/bprint.1 | 83 + reaction/engine/code/tools/lcc/doc/bprint.pdf | Bin 0 -> 4963 bytes .../engine/code/tools/lcc/doc/install.html | 796 ++ reaction/engine/code/tools/lcc/doc/lcc.1 | 605 ++ reaction/engine/code/tools/lcc/doc/lcc.pdf | Bin 0 -> 16421 bytes reaction/engine/code/tools/lcc/etc/bytecode.c | 66 + reaction/engine/code/tools/lcc/etc/lcc.c | 797 ++ reaction/engine/code/tools/lcc/lburg/gram.c | 639 ++ reaction/engine/code/tools/lcc/lburg/gram.y | 202 + reaction/engine/code/tools/lcc/lburg/lburg.1 | 179 + reaction/engine/code/tools/lcc/lburg/lburg.c | 671 ++ reaction/engine/code/tools/lcc/lburg/lburg.h | 65 + reaction/engine/code/tools/lcc/src/alloc.c | 94 + reaction/engine/code/tools/lcc/src/bind.c | 8 + reaction/engine/code/tools/lcc/src/bytecode.c | 367 + reaction/engine/code/tools/lcc/src/c.h | 729 ++ reaction/engine/code/tools/lcc/src/config.h | 102 + reaction/engine/code/tools/lcc/src/dag.c | 736 ++ .../engine/code/tools/lcc/src/dagcheck.md | 210 + reaction/engine/code/tools/lcc/src/decl.c | 1162 +++ reaction/engine/code/tools/lcc/src/enode.c | 545 ++ reaction/engine/code/tools/lcc/src/error.c | 137 + reaction/engine/code/tools/lcc/src/event.c | 28 + reaction/engine/code/tools/lcc/src/expr.c | 711 ++ reaction/engine/code/tools/lcc/src/gen.c | 830 +++ reaction/engine/code/tools/lcc/src/init.c | 318 + reaction/engine/code/tools/lcc/src/inits.c | 7 + reaction/engine/code/tools/lcc/src/input.c | 135 + reaction/engine/code/tools/lcc/src/lex.c | 923 +++ reaction/engine/code/tools/lcc/src/list.c | 56 + reaction/engine/code/tools/lcc/src/main.c | 225 + reaction/engine/code/tools/lcc/src/null.c | 74 + reaction/engine/code/tools/lcc/src/output.c | 135 + reaction/engine/code/tools/lcc/src/prof.c | 228 + reaction/engine/code/tools/lcc/src/profio.c | 276 + reaction/engine/code/tools/lcc/src/simp.c | 587 ++ reaction/engine/code/tools/lcc/src/stmt.c | 696 ++ reaction/engine/code/tools/lcc/src/string.c | 122 + reaction/engine/code/tools/lcc/src/sym.c | 314 + reaction/engine/code/tools/lcc/src/symbolic.c | 494 ++ reaction/engine/code/tools/lcc/src/token.h | 133 + reaction/engine/code/tools/lcc/src/trace.c | 181 + reaction/engine/code/tools/lcc/src/tree.c | 223 + reaction/engine/code/tools/lcc/src/types.c | 748 ++ reaction/engine/code/ui/ui_atoms.c | 519 ++ reaction/engine/code/ui/ui_gameinfo.c | 324 + reaction/engine/code/ui/ui_local.h | 1136 +++ reaction/engine/code/ui/ui_main.c | 5981 +++++++++++++++ reaction/engine/code/ui/ui_players.c | 1379 ++++ reaction/engine/code/ui/ui_public.h | 191 + reaction/engine/code/ui/ui_shared.c | 5789 +++++++++++++++ reaction/engine/code/ui/ui_shared.h | 450 ++ reaction/engine/code/ui/ui_syscalls.asm | 101 + reaction/engine/code/ui/ui_syscalls.c | 401 + reaction/engine/cross-make-mingw.sh | 6 + reaction/engine/id-readme.txt | 145 + reaction/engine/make-macosx-ub.sh | 238 + reaction/engine/md4-readme.txt | 54 + reaction/engine/misc/ReadMe-OSX.rtf | 29 + reaction/engine/misc/ioquake3-folder.icns | Bin 0 -> 280366 bytes reaction/engine/misc/msvc/cgame.def | 3 + reaction/engine/misc/msvc/cgame.vcproj | 1514 ++++ reaction/engine/misc/msvc/game.def | 3 + reaction/engine/misc/msvc/game.vcproj | 1893 +++++ reaction/engine/misc/msvc/ioq3.sln | 61 + reaction/engine/misc/msvc/q3_ui.def | 3 + reaction/engine/misc/msvc/q3_ui.vcproj | 2465 +++++++ reaction/engine/misc/msvc/quake3.vcproj | 3153 ++++++++ reaction/engine/misc/msvc/ui.def | 3 + reaction/engine/misc/msvc/ui.vcproj | 1018 +++ reaction/engine/misc/nsis/Makefile | 15 + reaction/engine/misc/nsis/ioquake3-q3a.nsi | 248 + reaction/engine/misc/nsis/ioquake3-q3ctc.nsi | 140 + reaction/engine/misc/nsis/ioquake3.nsi.in | 186 + .../engine/misc/osxfe/ioquake3fe/Controller.h | 22 + .../engine/misc/osxfe/ioquake3fe/Controller.m | 97 + .../ioquake3fe/English.lproj/ErrorWindow.xib | 473 ++ .../English.lproj/InfoPlist.strings | Bin 0 -> 92 bytes .../English.lproj/Localizable.strings | Bin 0 -> 470 bytes .../ioquake3fe/English.lproj/MainMenu.xib | 3209 ++++++++ .../misc/osxfe/ioquake3fe/ErrorWindow.h | 10 + .../misc/osxfe/ioquake3fe/ErrorWindow.m | 18 + .../osxfe/ioquake3fe/ErrorWindowController.h | 16 + .../osxfe/ioquake3fe/ErrorWindowController.m | 20 + .../engine/misc/osxfe/ioquake3fe/Info.plist | 28 + .../misc/osxfe/ioquake3fe/ioquake3.icns | Bin 0 -> 35224 bytes .../ioquake3fe.xcodeproj/TemplateIcon.icns | Bin 0 -> 52318 bytes .../ioquake3fe.xcodeproj/bw.mode1v3 | 1391 ++++ .../ioquake3fe.xcodeproj/bw.pbxuser | 204 + .../ioquake3fe.xcodeproj/project.pbxproj | 310 + .../osxfe/ioquake3fe/ioquake3fe_Prefix.pch | 7 + reaction/engine/misc/osxfe/ioquake3fe/main.m | 14 + reaction/engine/misc/quake3-tango.png | Bin 0 -> 30792 bytes reaction/engine/misc/quake3-tango.xcf | Bin 0 -> 229111 bytes reaction/engine/misc/quake3.icns | Bin 0 -> 35224 bytes reaction/engine/misc/quake3.ico | Bin 0 -> 65537 bytes reaction/engine/misc/quake3.ico-old | Bin 0 -> 25614 bytes reaction/engine/misc/quake3.png | Bin 0 -> 764 bytes reaction/engine/misc/quake3.svg | 67 + reaction/engine/misc/sdl-win32-fixes.diff | 596 ++ reaction/engine/misc/setup/MacOSX/SLA-dmg.sh | 73 + reaction/engine/misc/setup/Makefile | 15 + reaction/engine/misc/setup/Solaris_pkg.sh | 179 + reaction/engine/misc/setup/doit | 122 + .../misc/setup/install-desktop-files.sh | 14 + reaction/engine/misc/setup/ioq3demo.sh | 50 + .../engine/misc/setup/ioquake3.SlackBuild | 77 + reaction/engine/misc/setup/ioquake3.desktop | 9 + reaction/engine/misc/setup/ioquake3.sh | 50 + reaction/engine/misc/setup/pkg/ioq3ded.sh | 41 + reaction/engine/misc/setup/pkg/ioquake3.sh | 41 + .../engine/misc/setup/pkg/ioquake3/depend | 3 + .../misc/setup/pkg/ioquake3/pkginfo.template | 12 + .../misc/setup/pkg/ioquake3/postinstall | 21 + .../engine/misc/setup/pkg/ioquake3/postremove | 21 + .../engine/misc/setup/pkg/ioquake3/preinstall | 10 + .../engine/misc/setup/pkg/ioquake3/preremove | 10 + .../setup/pkg/ioquake3/prototype.template | 8 + reaction/engine/misc/setup/pkg/ioquake3/space | 1 + .../engine/misc/setup/pkg/ioquake3d/depend | 2 + .../misc/setup/pkg/ioquake3d/pkginfo.template | 12 + .../misc/setup/pkg/ioquake3d/postinstall | 10 + .../misc/setup/pkg/ioquake3d/postremove | 10 + .../misc/setup/pkg/ioquake3d/preinstall | 10 + .../engine/misc/setup/pkg/ioquake3d/preremove | 10 + .../setup/pkg/ioquake3d/prototype.template | 7 + .../engine/misc/setup/pkg/ioquake3d/space | 1 + .../engine/misc/setup/pkg/ioquake3m/depend | 2 + .../misc/setup/pkg/ioquake3m/pkginfo.template | 12 + .../misc/setup/pkg/ioquake3m/postinstall | 10 + .../misc/setup/pkg/ioquake3m/postremove | 10 + .../misc/setup/pkg/ioquake3m/preinstall | 10 + .../engine/misc/setup/pkg/ioquake3m/preremove | 10 + .../setup/pkg/ioquake3m/prototype.template | 7 + .../engine/misc/setup/pkg/ioquake3m/space | 1 + reaction/engine/misc/setup/preuninstall.sh | 10 + reaction/engine/misc/setup/setup.xml.in | 134 + reaction/engine/misc/setup/setup.xml.mod | 33 + reaction/engine/misc/setup/slack-desc | 12 + reaction/engine/misc/setup/splash.xpm | 2289 ++++++ reaction/engine/ui/hud.txt | 7 + reaction/engine/ui/hud2.txt | 7 + reaction/engine/ui/ingame.txt | 16 + reaction/engine/ui/menudef.h | 308 + reaction/engine/ui/menus.txt | 39 + reaction/engine/voip-readme.txt | 199 + reaction/enginediff | 353 + 734 files changed, 384869 insertions(+) create mode 100644 reaction/engine/.svnignore create mode 100644 reaction/engine/BUGS create mode 100644 reaction/engine/COPYING.txt create mode 100644 reaction/engine/ChangeLog create mode 100644 reaction/engine/Makefile create mode 100644 reaction/engine/NOTTODO create mode 100644 reaction/engine/README create mode 100644 reaction/engine/TODO create mode 100644 reaction/engine/code/AL/VERSION create mode 100644 reaction/engine/code/AL/al.h create mode 100644 reaction/engine/code/AL/alc.h create mode 100644 reaction/engine/code/AL/alctypes.h create mode 100644 reaction/engine/code/AL/altypes.h create mode 100644 reaction/engine/code/AL/alut.h create mode 100644 reaction/engine/code/SDL12/include/SDL.h create mode 100644 reaction/engine/code/SDL12/include/SDL_active.h create mode 100644 reaction/engine/code/SDL12/include/SDL_audio.h create mode 100644 reaction/engine/code/SDL12/include/SDL_byteorder.h create mode 100644 reaction/engine/code/SDL12/include/SDL_cdrom.h create mode 100644 reaction/engine/code/SDL12/include/SDL_config.h create mode 100644 reaction/engine/code/SDL12/include/SDL_config_amiga.h create mode 100644 reaction/engine/code/SDL12/include/SDL_config_dreamcast.h create mode 100644 reaction/engine/code/SDL12/include/SDL_config_macos.h create mode 100644 reaction/engine/code/SDL12/include/SDL_config_macosx.h create mode 100644 reaction/engine/code/SDL12/include/SDL_config_minimal.h create mode 100644 reaction/engine/code/SDL12/include/SDL_config_os2.h create mode 100644 reaction/engine/code/SDL12/include/SDL_config_win32.h create mode 100644 reaction/engine/code/SDL12/include/SDL_copying.h create mode 100644 reaction/engine/code/SDL12/include/SDL_cpuinfo.h create mode 100644 reaction/engine/code/SDL12/include/SDL_endian.h create mode 100644 reaction/engine/code/SDL12/include/SDL_error.h create mode 100644 reaction/engine/code/SDL12/include/SDL_events.h create mode 100644 reaction/engine/code/SDL12/include/SDL_getenv.h create mode 100644 reaction/engine/code/SDL12/include/SDL_joystick.h create mode 100644 reaction/engine/code/SDL12/include/SDL_keyboard.h create mode 100644 reaction/engine/code/SDL12/include/SDL_keysym.h create mode 100644 reaction/engine/code/SDL12/include/SDL_loadso.h create mode 100644 reaction/engine/code/SDL12/include/SDL_main.h create mode 100644 reaction/engine/code/SDL12/include/SDL_mouse.h create mode 100644 reaction/engine/code/SDL12/include/SDL_mutex.h create mode 100644 reaction/engine/code/SDL12/include/SDL_name.h create mode 100644 reaction/engine/code/SDL12/include/SDL_opengl.h create mode 100644 reaction/engine/code/SDL12/include/SDL_platform.h create mode 100644 reaction/engine/code/SDL12/include/SDL_quit.h create mode 100644 reaction/engine/code/SDL12/include/SDL_rwops.h create mode 100644 reaction/engine/code/SDL12/include/SDL_stdinc.h create mode 100644 reaction/engine/code/SDL12/include/SDL_syswm.h create mode 100644 reaction/engine/code/SDL12/include/SDL_thread.h create mode 100644 reaction/engine/code/SDL12/include/SDL_timer.h create mode 100644 reaction/engine/code/SDL12/include/SDL_types.h create mode 100644 reaction/engine/code/SDL12/include/SDL_version.h create mode 100644 reaction/engine/code/SDL12/include/SDL_video.h create mode 100644 reaction/engine/code/SDL12/include/begin_code.h create mode 100644 reaction/engine/code/SDL12/include/close_code.h create mode 100644 reaction/engine/code/asm/ftola.s create mode 100644 reaction/engine/code/asm/matha.s create mode 100644 reaction/engine/code/asm/qasm.h create mode 100644 reaction/engine/code/asm/snapvectora.s create mode 100644 reaction/engine/code/asm/snd_mixa.s create mode 100644 reaction/engine/code/botlib/aasfile.h create mode 100644 reaction/engine/code/botlib/be_aas.h create mode 100644 reaction/engine/code/botlib/be_aas_bsp.h create mode 100644 reaction/engine/code/botlib/be_aas_bspq3.c create mode 100644 reaction/engine/code/botlib/be_aas_cluster.c create mode 100644 reaction/engine/code/botlib/be_aas_cluster.h create mode 100644 reaction/engine/code/botlib/be_aas_debug.c create mode 100644 reaction/engine/code/botlib/be_aas_debug.h create mode 100644 reaction/engine/code/botlib/be_aas_def.h create mode 100644 reaction/engine/code/botlib/be_aas_entity.c create mode 100644 reaction/engine/code/botlib/be_aas_entity.h create mode 100644 reaction/engine/code/botlib/be_aas_file.c create mode 100644 reaction/engine/code/botlib/be_aas_file.h create mode 100644 reaction/engine/code/botlib/be_aas_funcs.h create mode 100644 reaction/engine/code/botlib/be_aas_main.c create mode 100644 reaction/engine/code/botlib/be_aas_main.h create mode 100644 reaction/engine/code/botlib/be_aas_move.c create mode 100644 reaction/engine/code/botlib/be_aas_move.h create mode 100644 reaction/engine/code/botlib/be_aas_optimize.c create mode 100644 reaction/engine/code/botlib/be_aas_optimize.h create mode 100644 reaction/engine/code/botlib/be_aas_reach.c create mode 100644 reaction/engine/code/botlib/be_aas_reach.h create mode 100644 reaction/engine/code/botlib/be_aas_route.c create mode 100644 reaction/engine/code/botlib/be_aas_route.h create mode 100644 reaction/engine/code/botlib/be_aas_routealt.c create mode 100644 reaction/engine/code/botlib/be_aas_routealt.h create mode 100644 reaction/engine/code/botlib/be_aas_sample.c create mode 100644 reaction/engine/code/botlib/be_aas_sample.h create mode 100644 reaction/engine/code/botlib/be_ai_char.c create mode 100644 reaction/engine/code/botlib/be_ai_char.h create mode 100644 reaction/engine/code/botlib/be_ai_chat.c create mode 100644 reaction/engine/code/botlib/be_ai_chat.h create mode 100644 reaction/engine/code/botlib/be_ai_gen.c create mode 100644 reaction/engine/code/botlib/be_ai_gen.h create mode 100644 reaction/engine/code/botlib/be_ai_goal.c create mode 100644 reaction/engine/code/botlib/be_ai_goal.h create mode 100644 reaction/engine/code/botlib/be_ai_move.c create mode 100644 reaction/engine/code/botlib/be_ai_move.h create mode 100644 reaction/engine/code/botlib/be_ai_weap.c create mode 100644 reaction/engine/code/botlib/be_ai_weap.h create mode 100644 reaction/engine/code/botlib/be_ai_weight.c create mode 100644 reaction/engine/code/botlib/be_ai_weight.h create mode 100644 reaction/engine/code/botlib/be_ea.c create mode 100644 reaction/engine/code/botlib/be_ea.h create mode 100644 reaction/engine/code/botlib/be_interface.c create mode 100644 reaction/engine/code/botlib/be_interface.h create mode 100644 reaction/engine/code/botlib/botlib.h create mode 100644 reaction/engine/code/botlib/l_crc.c create mode 100644 reaction/engine/code/botlib/l_crc.h create mode 100644 reaction/engine/code/botlib/l_libvar.c create mode 100644 reaction/engine/code/botlib/l_libvar.h create mode 100644 reaction/engine/code/botlib/l_log.c create mode 100644 reaction/engine/code/botlib/l_log.h create mode 100644 reaction/engine/code/botlib/l_memory.c create mode 100644 reaction/engine/code/botlib/l_memory.h create mode 100644 reaction/engine/code/botlib/l_precomp.c create mode 100644 reaction/engine/code/botlib/l_precomp.h create mode 100644 reaction/engine/code/botlib/l_script.c create mode 100644 reaction/engine/code/botlib/l_script.h create mode 100644 reaction/engine/code/botlib/l_struct.c create mode 100644 reaction/engine/code/botlib/l_struct.h create mode 100644 reaction/engine/code/botlib/l_utils.h create mode 100644 reaction/engine/code/botlib/lcc.mak create mode 100644 reaction/engine/code/botlib/linux-i386.mak create mode 100644 reaction/engine/code/cgame/cg_consolecmds.c create mode 100644 reaction/engine/code/cgame/cg_draw.c create mode 100644 reaction/engine/code/cgame/cg_drawtools.c create mode 100644 reaction/engine/code/cgame/cg_effects.c create mode 100644 reaction/engine/code/cgame/cg_ents.c create mode 100644 reaction/engine/code/cgame/cg_event.c create mode 100644 reaction/engine/code/cgame/cg_info.c create mode 100644 reaction/engine/code/cgame/cg_local.h create mode 100644 reaction/engine/code/cgame/cg_localents.c create mode 100644 reaction/engine/code/cgame/cg_main.c create mode 100644 reaction/engine/code/cgame/cg_marks.c create mode 100644 reaction/engine/code/cgame/cg_newdraw.c create mode 100644 reaction/engine/code/cgame/cg_particles.c create mode 100644 reaction/engine/code/cgame/cg_players.c create mode 100644 reaction/engine/code/cgame/cg_playerstate.c create mode 100644 reaction/engine/code/cgame/cg_predict.c create mode 100644 reaction/engine/code/cgame/cg_public.h create mode 100644 reaction/engine/code/cgame/cg_scoreboard.c create mode 100644 reaction/engine/code/cgame/cg_servercmds.c create mode 100644 reaction/engine/code/cgame/cg_snapshot.c create mode 100644 reaction/engine/code/cgame/cg_syscalls.asm create mode 100644 reaction/engine/code/cgame/cg_syscalls.c create mode 100644 reaction/engine/code/cgame/cg_view.c create mode 100644 reaction/engine/code/cgame/cg_weapons.c create mode 100644 reaction/engine/code/client/cl_avi.c create mode 100644 reaction/engine/code/client/cl_cgame.c create mode 100644 reaction/engine/code/client/cl_cin.c create mode 100644 reaction/engine/code/client/cl_console.c create mode 100644 reaction/engine/code/client/cl_curl.c create mode 100644 reaction/engine/code/client/cl_curl.h create mode 100644 reaction/engine/code/client/cl_input.c create mode 100644 reaction/engine/code/client/cl_keys.c create mode 100644 reaction/engine/code/client/cl_main.c create mode 100644 reaction/engine/code/client/cl_net_chan.c create mode 100644 reaction/engine/code/client/cl_parse.c create mode 100644 reaction/engine/code/client/cl_scrn.c create mode 100644 reaction/engine/code/client/cl_ui.c create mode 100644 reaction/engine/code/client/client.h create mode 100644 reaction/engine/code/client/keycodes.h create mode 100644 reaction/engine/code/client/keys.h create mode 100644 reaction/engine/code/client/libmumblelink.c create mode 100644 reaction/engine/code/client/libmumblelink.h create mode 100644 reaction/engine/code/client/qal.c create mode 100644 reaction/engine/code/client/qal.h create mode 100644 reaction/engine/code/client/snd_adpcm.c create mode 100644 reaction/engine/code/client/snd_codec.c create mode 100644 reaction/engine/code/client/snd_codec.h create mode 100644 reaction/engine/code/client/snd_codec_ogg.c create mode 100644 reaction/engine/code/client/snd_codec_wav.c create mode 100644 reaction/engine/code/client/snd_dma.c create mode 100644 reaction/engine/code/client/snd_local.h create mode 100644 reaction/engine/code/client/snd_main.c create mode 100644 reaction/engine/code/client/snd_mem.c create mode 100644 reaction/engine/code/client/snd_mix.c create mode 100644 reaction/engine/code/client/snd_openal.c create mode 100644 reaction/engine/code/client/snd_public.h create mode 100644 reaction/engine/code/client/snd_wavelet.c create mode 100644 reaction/engine/code/game/ai_chat.c create mode 100644 reaction/engine/code/game/ai_chat.h create mode 100644 reaction/engine/code/game/ai_cmd.c create mode 100644 reaction/engine/code/game/ai_cmd.h create mode 100644 reaction/engine/code/game/ai_dmnet.c create mode 100644 reaction/engine/code/game/ai_dmnet.h create mode 100644 reaction/engine/code/game/ai_dmq3.c create mode 100644 reaction/engine/code/game/ai_dmq3.h create mode 100644 reaction/engine/code/game/ai_main.c create mode 100644 reaction/engine/code/game/ai_main.h create mode 100644 reaction/engine/code/game/ai_team.c create mode 100644 reaction/engine/code/game/ai_team.h create mode 100644 reaction/engine/code/game/ai_vcmd.c create mode 100644 reaction/engine/code/game/ai_vcmd.h create mode 100644 reaction/engine/code/game/bg_lib.c create mode 100644 reaction/engine/code/game/bg_lib.h create mode 100644 reaction/engine/code/game/bg_local.h create mode 100644 reaction/engine/code/game/bg_misc.c create mode 100644 reaction/engine/code/game/bg_pmove.c create mode 100644 reaction/engine/code/game/bg_public.h create mode 100644 reaction/engine/code/game/bg_slidemove.c create mode 100644 reaction/engine/code/game/chars.h create mode 100644 reaction/engine/code/game/g_active.c create mode 100644 reaction/engine/code/game/g_arenas.c create mode 100644 reaction/engine/code/game/g_bot.c create mode 100644 reaction/engine/code/game/g_client.c create mode 100644 reaction/engine/code/game/g_cmds.c create mode 100644 reaction/engine/code/game/g_combat.c create mode 100644 reaction/engine/code/game/g_items.c create mode 100644 reaction/engine/code/game/g_local.h create mode 100644 reaction/engine/code/game/g_main.c create mode 100644 reaction/engine/code/game/g_mem.c create mode 100644 reaction/engine/code/game/g_misc.c create mode 100644 reaction/engine/code/game/g_missile.c create mode 100644 reaction/engine/code/game/g_mover.c create mode 100644 reaction/engine/code/game/g_public.h create mode 100644 reaction/engine/code/game/g_rankings.c create mode 100644 reaction/engine/code/game/g_rankings.h create mode 100644 reaction/engine/code/game/g_session.c create mode 100644 reaction/engine/code/game/g_spawn.c create mode 100644 reaction/engine/code/game/g_svcmds.c create mode 100644 reaction/engine/code/game/g_syscalls.asm create mode 100644 reaction/engine/code/game/g_syscalls.c create mode 100644 reaction/engine/code/game/g_target.c create mode 100644 reaction/engine/code/game/g_team.c create mode 100644 reaction/engine/code/game/g_team.h create mode 100644 reaction/engine/code/game/g_trigger.c create mode 100644 reaction/engine/code/game/g_utils.c create mode 100644 reaction/engine/code/game/g_weapon.c create mode 100644 reaction/engine/code/game/inv.h create mode 100644 reaction/engine/code/game/match.h create mode 100644 reaction/engine/code/game/syn.h create mode 100644 reaction/engine/code/jpeg-6b/README create mode 100644 reaction/engine/code/jpeg-6b/ioq3-changes.diff create mode 100644 reaction/engine/code/jpeg-6b/jcapimin.c create mode 100644 reaction/engine/code/jpeg-6b/jcapistd.c create mode 100644 reaction/engine/code/jpeg-6b/jccoefct.c create mode 100644 reaction/engine/code/jpeg-6b/jccolor.c create mode 100644 reaction/engine/code/jpeg-6b/jcdctmgr.c create mode 100644 reaction/engine/code/jpeg-6b/jchuff.c create mode 100644 reaction/engine/code/jpeg-6b/jchuff.h create mode 100644 reaction/engine/code/jpeg-6b/jcinit.c create mode 100644 reaction/engine/code/jpeg-6b/jcmainct.c create mode 100644 reaction/engine/code/jpeg-6b/jcmarker.c create mode 100644 reaction/engine/code/jpeg-6b/jcmaster.c create mode 100644 reaction/engine/code/jpeg-6b/jcomapi.c create mode 100644 reaction/engine/code/jpeg-6b/jconfig.h create mode 100644 reaction/engine/code/jpeg-6b/jcparam.c create mode 100644 reaction/engine/code/jpeg-6b/jcphuff.c create mode 100644 reaction/engine/code/jpeg-6b/jcprepct.c create mode 100644 reaction/engine/code/jpeg-6b/jcsample.c create mode 100644 reaction/engine/code/jpeg-6b/jctrans.c create mode 100644 reaction/engine/code/jpeg-6b/jdapimin.c create mode 100644 reaction/engine/code/jpeg-6b/jdapistd.c create mode 100644 reaction/engine/code/jpeg-6b/jdatadst.c create mode 100644 reaction/engine/code/jpeg-6b/jdatasrc.c create mode 100644 reaction/engine/code/jpeg-6b/jdcoefct.c create mode 100644 reaction/engine/code/jpeg-6b/jdcolor.c create mode 100644 reaction/engine/code/jpeg-6b/jdct.h create mode 100644 reaction/engine/code/jpeg-6b/jddctmgr.c create mode 100644 reaction/engine/code/jpeg-6b/jdhuff.c create mode 100644 reaction/engine/code/jpeg-6b/jdhuff.h create mode 100644 reaction/engine/code/jpeg-6b/jdinput.c create mode 100644 reaction/engine/code/jpeg-6b/jdmainct.c create mode 100644 reaction/engine/code/jpeg-6b/jdmarker.c create mode 100644 reaction/engine/code/jpeg-6b/jdmaster.c create mode 100644 reaction/engine/code/jpeg-6b/jdmerge.c create mode 100644 reaction/engine/code/jpeg-6b/jdphuff.c create mode 100644 reaction/engine/code/jpeg-6b/jdpostct.c create mode 100644 reaction/engine/code/jpeg-6b/jdsample.c create mode 100644 reaction/engine/code/jpeg-6b/jdtrans.c create mode 100644 reaction/engine/code/jpeg-6b/jerror.c create mode 100644 reaction/engine/code/jpeg-6b/jerror.h create mode 100644 reaction/engine/code/jpeg-6b/jfdctflt.c create mode 100644 reaction/engine/code/jpeg-6b/jfdctfst.c create mode 100644 reaction/engine/code/jpeg-6b/jfdctint.c create mode 100644 reaction/engine/code/jpeg-6b/jidctflt.c create mode 100644 reaction/engine/code/jpeg-6b/jidctfst.c create mode 100644 reaction/engine/code/jpeg-6b/jidctint.c create mode 100644 reaction/engine/code/jpeg-6b/jidctred.c create mode 100644 reaction/engine/code/jpeg-6b/jinclude.h create mode 100644 reaction/engine/code/jpeg-6b/jload.c create mode 100644 reaction/engine/code/jpeg-6b/jmemansi.c create mode 100644 reaction/engine/code/jpeg-6b/jmemdos.c create mode 100644 reaction/engine/code/jpeg-6b/jmemmgr.c create mode 100644 reaction/engine/code/jpeg-6b/jmemname.c create mode 100644 reaction/engine/code/jpeg-6b/jmemnobs.c create mode 100644 reaction/engine/code/jpeg-6b/jmemsys.h create mode 100644 reaction/engine/code/jpeg-6b/jmorecfg.h create mode 100644 reaction/engine/code/jpeg-6b/jpegint.h create mode 100644 reaction/engine/code/jpeg-6b/jpeglib.h create mode 100644 reaction/engine/code/jpeg-6b/jpegtran.c create mode 100644 reaction/engine/code/jpeg-6b/jquant1.c create mode 100644 reaction/engine/code/jpeg-6b/jquant2.c create mode 100644 reaction/engine/code/jpeg-6b/jutils.c create mode 100644 reaction/engine/code/jpeg-6b/jversion.h create mode 100644 reaction/engine/code/libcurl/curl/curl.h create mode 100644 reaction/engine/code/libcurl/curl/curlver.h create mode 100644 reaction/engine/code/libcurl/curl/easy.h create mode 100644 reaction/engine/code/libcurl/curl/mprintf.h create mode 100644 reaction/engine/code/libcurl/curl/multi.h create mode 100644 reaction/engine/code/libcurl/curl/stdcheaders.h create mode 100644 reaction/engine/code/libcurl/curl/types.h create mode 100755 reaction/engine/code/libs/macosx/libSDL-1.2.0.dylib create mode 100644 reaction/engine/code/libspeex/_kiss_fft_guts.h create mode 100644 reaction/engine/code/libspeex/arch.h create mode 100644 reaction/engine/code/libspeex/bits.c create mode 100644 reaction/engine/code/libspeex/buffer.c create mode 100644 reaction/engine/code/libspeex/cb_search.c create mode 100644 reaction/engine/code/libspeex/cb_search.h create mode 100644 reaction/engine/code/libspeex/cb_search_arm4.h create mode 100644 reaction/engine/code/libspeex/cb_search_bfin.h create mode 100644 reaction/engine/code/libspeex/cb_search_sse.h create mode 100644 reaction/engine/code/libspeex/config.h create mode 100644 reaction/engine/code/libspeex/echo_diagnostic.m create mode 100644 reaction/engine/code/libspeex/exc_10_16_table.c create mode 100644 reaction/engine/code/libspeex/exc_10_32_table.c create mode 100644 reaction/engine/code/libspeex/exc_20_32_table.c create mode 100644 reaction/engine/code/libspeex/exc_5_256_table.c create mode 100644 reaction/engine/code/libspeex/exc_5_64_table.c create mode 100644 reaction/engine/code/libspeex/exc_8_128_table.c create mode 100644 reaction/engine/code/libspeex/fftwrap.c create mode 100644 reaction/engine/code/libspeex/fftwrap.h create mode 100644 reaction/engine/code/libspeex/filterbank.c create mode 100644 reaction/engine/code/libspeex/filterbank.h create mode 100644 reaction/engine/code/libspeex/filters.c create mode 100644 reaction/engine/code/libspeex/filters.h create mode 100644 reaction/engine/code/libspeex/filters_arm4.h create mode 100644 reaction/engine/code/libspeex/filters_bfin.h create mode 100644 reaction/engine/code/libspeex/filters_sse.h create mode 100644 reaction/engine/code/libspeex/fixed_arm4.h create mode 100644 reaction/engine/code/libspeex/fixed_arm5e.h create mode 100644 reaction/engine/code/libspeex/fixed_bfin.h create mode 100644 reaction/engine/code/libspeex/fixed_debug.h create mode 100644 reaction/engine/code/libspeex/fixed_generic.h create mode 100644 reaction/engine/code/libspeex/gain_table.c create mode 100644 reaction/engine/code/libspeex/gain_table_lbr.c create mode 100644 reaction/engine/code/libspeex/hexc_10_32_table.c create mode 100644 reaction/engine/code/libspeex/hexc_table.c create mode 100644 reaction/engine/code/libspeex/high_lsp_tables.c create mode 100644 reaction/engine/code/libspeex/include/speex/speex.h create mode 100644 reaction/engine/code/libspeex/include/speex/speex_bits.h create mode 100644 reaction/engine/code/libspeex/include/speex/speex_buffer.h create mode 100644 reaction/engine/code/libspeex/include/speex/speex_callbacks.h create mode 100644 reaction/engine/code/libspeex/include/speex/speex_config_types.h create mode 100644 reaction/engine/code/libspeex/include/speex/speex_echo.h create mode 100644 reaction/engine/code/libspeex/include/speex/speex_header.h create mode 100644 reaction/engine/code/libspeex/include/speex/speex_jitter.h create mode 100644 reaction/engine/code/libspeex/include/speex/speex_preprocess.h create mode 100644 reaction/engine/code/libspeex/include/speex/speex_resampler.h create mode 100644 reaction/engine/code/libspeex/include/speex/speex_stereo.h create mode 100644 reaction/engine/code/libspeex/include/speex/speex_types.h create mode 100644 reaction/engine/code/libspeex/jitter.c create mode 100644 reaction/engine/code/libspeex/kiss_fft.c create mode 100644 reaction/engine/code/libspeex/kiss_fft.h create mode 100644 reaction/engine/code/libspeex/kiss_fftr.c create mode 100644 reaction/engine/code/libspeex/kiss_fftr.h create mode 100644 reaction/engine/code/libspeex/lpc.c create mode 100644 reaction/engine/code/libspeex/lpc.h create mode 100644 reaction/engine/code/libspeex/lpc_bfin.h create mode 100644 reaction/engine/code/libspeex/lsp.c create mode 100644 reaction/engine/code/libspeex/lsp.h create mode 100644 reaction/engine/code/libspeex/lsp_bfin.h create mode 100644 reaction/engine/code/libspeex/lsp_tables_nb.c create mode 100644 reaction/engine/code/libspeex/ltp.c create mode 100644 reaction/engine/code/libspeex/ltp.h create mode 100644 reaction/engine/code/libspeex/ltp_arm4.h create mode 100644 reaction/engine/code/libspeex/ltp_bfin.h create mode 100644 reaction/engine/code/libspeex/ltp_sse.h create mode 100644 reaction/engine/code/libspeex/math_approx.h create mode 100644 reaction/engine/code/libspeex/mdf.c create mode 100644 reaction/engine/code/libspeex/misc_bfin.h create mode 100644 reaction/engine/code/libspeex/modes.c create mode 100644 reaction/engine/code/libspeex/modes.h create mode 100644 reaction/engine/code/libspeex/modes_wb.c create mode 100644 reaction/engine/code/libspeex/nb_celp.c create mode 100644 reaction/engine/code/libspeex/nb_celp.h create mode 100644 reaction/engine/code/libspeex/os_support.h create mode 100644 reaction/engine/code/libspeex/preprocess.c create mode 100644 reaction/engine/code/libspeex/pseudofloat.h create mode 100644 reaction/engine/code/libspeex/quant_lsp.c create mode 100644 reaction/engine/code/libspeex/quant_lsp.h create mode 100644 reaction/engine/code/libspeex/quant_lsp_bfin.h create mode 100644 reaction/engine/code/libspeex/resample.c create mode 100644 reaction/engine/code/libspeex/sb_celp.c create mode 100644 reaction/engine/code/libspeex/sb_celp.h create mode 100644 reaction/engine/code/libspeex/smallft.c create mode 100644 reaction/engine/code/libspeex/smallft.h create mode 100644 reaction/engine/code/libspeex/speex.c create mode 100644 reaction/engine/code/libspeex/speex_callbacks.c create mode 100644 reaction/engine/code/libspeex/speex_header.c create mode 100644 reaction/engine/code/libspeex/stack_alloc.h create mode 100644 reaction/engine/code/libspeex/stereo.c create mode 100644 reaction/engine/code/libspeex/testdenoise.c create mode 100644 reaction/engine/code/libspeex/testecho.c create mode 100644 reaction/engine/code/libspeex/testenc.c create mode 100644 reaction/engine/code/libspeex/testenc_uwb.c create mode 100644 reaction/engine/code/libspeex/testenc_wb.c create mode 100644 reaction/engine/code/libspeex/vbr.c create mode 100644 reaction/engine/code/libspeex/vbr.h create mode 100644 reaction/engine/code/libspeex/vorbis_psy.h create mode 100644 reaction/engine/code/libspeex/vq.c create mode 100644 reaction/engine/code/libspeex/vq.h create mode 100644 reaction/engine/code/libspeex/vq_arm4.h create mode 100644 reaction/engine/code/libspeex/vq_bfin.h create mode 100644 reaction/engine/code/libspeex/vq_sse.h create mode 100644 reaction/engine/code/libspeex/window.c create mode 100644 reaction/engine/code/null/mac_net.c create mode 100644 reaction/engine/code/null/null_client.c create mode 100644 reaction/engine/code/null/null_glimp.c create mode 100644 reaction/engine/code/null/null_input.c create mode 100644 reaction/engine/code/null/null_main.c create mode 100644 reaction/engine/code/null/null_net.c create mode 100644 reaction/engine/code/null/null_snddma.c create mode 100644 reaction/engine/code/q3_ui/ui_addbots.c create mode 100644 reaction/engine/code/q3_ui/ui_atoms.c create mode 100644 reaction/engine/code/q3_ui/ui_cdkey.c create mode 100644 reaction/engine/code/q3_ui/ui_cinematics.c create mode 100644 reaction/engine/code/q3_ui/ui_confirm.c create mode 100644 reaction/engine/code/q3_ui/ui_connect.c create mode 100644 reaction/engine/code/q3_ui/ui_controls2.c create mode 100644 reaction/engine/code/q3_ui/ui_credits.c create mode 100644 reaction/engine/code/q3_ui/ui_demo2.c create mode 100644 reaction/engine/code/q3_ui/ui_display.c create mode 100644 reaction/engine/code/q3_ui/ui_gameinfo.c create mode 100644 reaction/engine/code/q3_ui/ui_ingame.c create mode 100644 reaction/engine/code/q3_ui/ui_loadconfig.c create mode 100644 reaction/engine/code/q3_ui/ui_local.h create mode 100644 reaction/engine/code/q3_ui/ui_login.c create mode 100644 reaction/engine/code/q3_ui/ui_main.c create mode 100644 reaction/engine/code/q3_ui/ui_menu.c create mode 100644 reaction/engine/code/q3_ui/ui_mfield.c create mode 100644 reaction/engine/code/q3_ui/ui_mods.c create mode 100644 reaction/engine/code/q3_ui/ui_network.c create mode 100644 reaction/engine/code/q3_ui/ui_options.c create mode 100644 reaction/engine/code/q3_ui/ui_playermodel.c create mode 100644 reaction/engine/code/q3_ui/ui_players.c create mode 100644 reaction/engine/code/q3_ui/ui_playersettings.c create mode 100644 reaction/engine/code/q3_ui/ui_preferences.c create mode 100644 reaction/engine/code/q3_ui/ui_qmenu.c create mode 100644 reaction/engine/code/q3_ui/ui_rankings.c create mode 100644 reaction/engine/code/q3_ui/ui_rankstatus.c create mode 100644 reaction/engine/code/q3_ui/ui_removebots.c create mode 100644 reaction/engine/code/q3_ui/ui_saveconfig.c create mode 100644 reaction/engine/code/q3_ui/ui_serverinfo.c create mode 100644 reaction/engine/code/q3_ui/ui_servers2.c create mode 100644 reaction/engine/code/q3_ui/ui_setup.c create mode 100644 reaction/engine/code/q3_ui/ui_signup.c create mode 100644 reaction/engine/code/q3_ui/ui_sound.c create mode 100644 reaction/engine/code/q3_ui/ui_sparena.c create mode 100644 reaction/engine/code/q3_ui/ui_specifyleague.c create mode 100644 reaction/engine/code/q3_ui/ui_specifyserver.c create mode 100644 reaction/engine/code/q3_ui/ui_splevel.c create mode 100644 reaction/engine/code/q3_ui/ui_sppostgame.c create mode 100644 reaction/engine/code/q3_ui/ui_spreset.c create mode 100644 reaction/engine/code/q3_ui/ui_spskill.c create mode 100644 reaction/engine/code/q3_ui/ui_startserver.c create mode 100644 reaction/engine/code/q3_ui/ui_team.c create mode 100644 reaction/engine/code/q3_ui/ui_teamorders.c create mode 100644 reaction/engine/code/q3_ui/ui_video.c create mode 100644 reaction/engine/code/qcommon/cm_load.c create mode 100644 reaction/engine/code/qcommon/cm_local.h create mode 100644 reaction/engine/code/qcommon/cm_patch.c create mode 100644 reaction/engine/code/qcommon/cm_patch.h create mode 100644 reaction/engine/code/qcommon/cm_polylib.c create mode 100644 reaction/engine/code/qcommon/cm_polylib.h create mode 100644 reaction/engine/code/qcommon/cm_public.h create mode 100644 reaction/engine/code/qcommon/cm_test.c create mode 100644 reaction/engine/code/qcommon/cm_trace.c create mode 100644 reaction/engine/code/qcommon/cmd.c create mode 100644 reaction/engine/code/qcommon/common.c create mode 100644 reaction/engine/code/qcommon/cvar.c create mode 100644 reaction/engine/code/qcommon/files.c create mode 100644 reaction/engine/code/qcommon/huffman.c create mode 100644 reaction/engine/code/qcommon/md4.c create mode 100644 reaction/engine/code/qcommon/md5.c create mode 100644 reaction/engine/code/qcommon/msg.c create mode 100644 reaction/engine/code/qcommon/net_chan.c create mode 100644 reaction/engine/code/qcommon/net_ip.c create mode 100644 reaction/engine/code/qcommon/puff.c create mode 100644 reaction/engine/code/qcommon/puff.h create mode 100644 reaction/engine/code/qcommon/q_math.c create mode 100644 reaction/engine/code/qcommon/q_platform.h create mode 100644 reaction/engine/code/qcommon/q_shared.c create mode 100644 reaction/engine/code/qcommon/q_shared.h create mode 100644 reaction/engine/code/qcommon/qcommon.h create mode 100644 reaction/engine/code/qcommon/qfiles.h create mode 100644 reaction/engine/code/qcommon/surfaceflags.h create mode 100644 reaction/engine/code/qcommon/unzip.c create mode 100644 reaction/engine/code/qcommon/unzip.h create mode 100644 reaction/engine/code/qcommon/vm.c create mode 100644 reaction/engine/code/qcommon/vm_interpreted.c create mode 100644 reaction/engine/code/qcommon/vm_local.h create mode 100644 reaction/engine/code/qcommon/vm_none.c create mode 100644 reaction/engine/code/qcommon/vm_powerpc.c create mode 100644 reaction/engine/code/qcommon/vm_powerpc_asm.c create mode 100644 reaction/engine/code/qcommon/vm_powerpc_asm.h create mode 100644 reaction/engine/code/qcommon/vm_ppc.c create mode 100644 reaction/engine/code/qcommon/vm_sparc.c create mode 100644 reaction/engine/code/qcommon/vm_sparc.h create mode 100644 reaction/engine/code/qcommon/vm_x86.c create mode 100644 reaction/engine/code/qcommon/vm_x86_64.c create mode 100644 reaction/engine/code/qcommon/vm_x86_64_assembler.c create mode 100644 reaction/engine/code/renderer/qgl.h create mode 100644 reaction/engine/code/renderer/tr_animation.c create mode 100644 reaction/engine/code/renderer/tr_backend.c create mode 100644 reaction/engine/code/renderer/tr_bsp.c create mode 100644 reaction/engine/code/renderer/tr_cmds.c create mode 100644 reaction/engine/code/renderer/tr_curve.c create mode 100644 reaction/engine/code/renderer/tr_flares.c create mode 100644 reaction/engine/code/renderer/tr_font.c create mode 100644 reaction/engine/code/renderer/tr_image.c create mode 100644 reaction/engine/code/renderer/tr_image_bmp.c create mode 100644 reaction/engine/code/renderer/tr_image_jpg.c create mode 100644 reaction/engine/code/renderer/tr_image_pcx.c create mode 100644 reaction/engine/code/renderer/tr_image_png.c create mode 100644 reaction/engine/code/renderer/tr_image_tga.c create mode 100644 reaction/engine/code/renderer/tr_init.c create mode 100644 reaction/engine/code/renderer/tr_light.c create mode 100644 reaction/engine/code/renderer/tr_local.h create mode 100644 reaction/engine/code/renderer/tr_main.c create mode 100644 reaction/engine/code/renderer/tr_marks.c create mode 100644 reaction/engine/code/renderer/tr_mesh.c create mode 100644 reaction/engine/code/renderer/tr_model.c create mode 100644 reaction/engine/code/renderer/tr_noise.c create mode 100644 reaction/engine/code/renderer/tr_public.h create mode 100644 reaction/engine/code/renderer/tr_scene.c create mode 100644 reaction/engine/code/renderer/tr_shade.c create mode 100644 reaction/engine/code/renderer/tr_shade_calc.c create mode 100644 reaction/engine/code/renderer/tr_shader.c create mode 100644 reaction/engine/code/renderer/tr_shadows.c create mode 100644 reaction/engine/code/renderer/tr_sky.c create mode 100644 reaction/engine/code/renderer/tr_surface.c create mode 100644 reaction/engine/code/renderer/tr_types.h create mode 100644 reaction/engine/code/renderer/tr_world.c create mode 100644 reaction/engine/code/sdl/sdl_gamma.c create mode 100644 reaction/engine/code/sdl/sdl_glimp.c create mode 100644 reaction/engine/code/sdl/sdl_icon.h create mode 100644 reaction/engine/code/sdl/sdl_input.c create mode 100644 reaction/engine/code/sdl/sdl_snd.c create mode 100644 reaction/engine/code/server/server.h create mode 100644 reaction/engine/code/server/sv_bot.c create mode 100644 reaction/engine/code/server/sv_ccmds.c create mode 100644 reaction/engine/code/server/sv_client.c create mode 100644 reaction/engine/code/server/sv_game.c create mode 100644 reaction/engine/code/server/sv_init.c create mode 100644 reaction/engine/code/server/sv_main.c create mode 100644 reaction/engine/code/server/sv_net_chan.c create mode 100644 reaction/engine/code/server/sv_rankings.c create mode 100644 reaction/engine/code/server/sv_snapshot.c create mode 100644 reaction/engine/code/server/sv_world.c create mode 100644 reaction/engine/code/sys/con_log.c create mode 100644 reaction/engine/code/sys/con_passive.c create mode 100644 reaction/engine/code/sys/con_tty.c create mode 100644 reaction/engine/code/sys/con_win32.c create mode 100644 reaction/engine/code/sys/sys_loadlib.h create mode 100644 reaction/engine/code/sys/sys_local.h create mode 100644 reaction/engine/code/sys/sys_main.c create mode 100644 reaction/engine/code/sys/sys_unix.c create mode 100644 reaction/engine/code/sys/sys_win32.c create mode 100644 reaction/engine/code/sys/win_resource.h create mode 100644 reaction/engine/code/sys/win_resource.rc create mode 100644 reaction/engine/code/tools/asm/README.Id create mode 100644 reaction/engine/code/tools/asm/cmdlib.c create mode 100644 reaction/engine/code/tools/asm/cmdlib.h create mode 100644 reaction/engine/code/tools/asm/lib.txt create mode 100644 reaction/engine/code/tools/asm/mathlib.h create mode 100644 reaction/engine/code/tools/asm/notes.txt create mode 100644 reaction/engine/code/tools/asm/ops.txt create mode 100644 reaction/engine/code/tools/asm/opstrings.h create mode 100644 reaction/engine/code/tools/asm/q3asm.c create mode 100644 reaction/engine/code/tools/lcc/COPYRIGHT create mode 100644 reaction/engine/code/tools/lcc/LOG create mode 100644 reaction/engine/code/tools/lcc/README create mode 100644 reaction/engine/code/tools/lcc/README.id create mode 100644 reaction/engine/code/tools/lcc/cpp/cpp.c create mode 100644 reaction/engine/code/tools/lcc/cpp/cpp.h create mode 100644 reaction/engine/code/tools/lcc/cpp/eval.c create mode 100644 reaction/engine/code/tools/lcc/cpp/getopt.c create mode 100644 reaction/engine/code/tools/lcc/cpp/hideset.c create mode 100644 reaction/engine/code/tools/lcc/cpp/include.c create mode 100644 reaction/engine/code/tools/lcc/cpp/lex.c create mode 100644 reaction/engine/code/tools/lcc/cpp/macro.c create mode 100644 reaction/engine/code/tools/lcc/cpp/nlist.c create mode 100644 reaction/engine/code/tools/lcc/cpp/tokens.c create mode 100644 reaction/engine/code/tools/lcc/cpp/unix.c create mode 100644 reaction/engine/code/tools/lcc/doc/4.html create mode 100644 reaction/engine/code/tools/lcc/doc/bprint.1 create mode 100644 reaction/engine/code/tools/lcc/doc/bprint.pdf create mode 100644 reaction/engine/code/tools/lcc/doc/install.html create mode 100644 reaction/engine/code/tools/lcc/doc/lcc.1 create mode 100644 reaction/engine/code/tools/lcc/doc/lcc.pdf create mode 100644 reaction/engine/code/tools/lcc/etc/bytecode.c create mode 100644 reaction/engine/code/tools/lcc/etc/lcc.c create mode 100644 reaction/engine/code/tools/lcc/lburg/gram.c create mode 100644 reaction/engine/code/tools/lcc/lburg/gram.y create mode 100644 reaction/engine/code/tools/lcc/lburg/lburg.1 create mode 100644 reaction/engine/code/tools/lcc/lburg/lburg.c create mode 100644 reaction/engine/code/tools/lcc/lburg/lburg.h create mode 100644 reaction/engine/code/tools/lcc/src/alloc.c create mode 100644 reaction/engine/code/tools/lcc/src/bind.c create mode 100644 reaction/engine/code/tools/lcc/src/bytecode.c create mode 100644 reaction/engine/code/tools/lcc/src/c.h create mode 100644 reaction/engine/code/tools/lcc/src/config.h create mode 100644 reaction/engine/code/tools/lcc/src/dag.c create mode 100644 reaction/engine/code/tools/lcc/src/dagcheck.md create mode 100644 reaction/engine/code/tools/lcc/src/decl.c create mode 100644 reaction/engine/code/tools/lcc/src/enode.c create mode 100644 reaction/engine/code/tools/lcc/src/error.c create mode 100644 reaction/engine/code/tools/lcc/src/event.c create mode 100644 reaction/engine/code/tools/lcc/src/expr.c create mode 100644 reaction/engine/code/tools/lcc/src/gen.c create mode 100644 reaction/engine/code/tools/lcc/src/init.c create mode 100644 reaction/engine/code/tools/lcc/src/inits.c create mode 100644 reaction/engine/code/tools/lcc/src/input.c create mode 100644 reaction/engine/code/tools/lcc/src/lex.c create mode 100644 reaction/engine/code/tools/lcc/src/list.c create mode 100644 reaction/engine/code/tools/lcc/src/main.c create mode 100644 reaction/engine/code/tools/lcc/src/null.c create mode 100644 reaction/engine/code/tools/lcc/src/output.c create mode 100644 reaction/engine/code/tools/lcc/src/prof.c create mode 100644 reaction/engine/code/tools/lcc/src/profio.c create mode 100644 reaction/engine/code/tools/lcc/src/simp.c create mode 100644 reaction/engine/code/tools/lcc/src/stmt.c create mode 100644 reaction/engine/code/tools/lcc/src/string.c create mode 100644 reaction/engine/code/tools/lcc/src/sym.c create mode 100644 reaction/engine/code/tools/lcc/src/symbolic.c create mode 100644 reaction/engine/code/tools/lcc/src/token.h create mode 100644 reaction/engine/code/tools/lcc/src/trace.c create mode 100644 reaction/engine/code/tools/lcc/src/tree.c create mode 100644 reaction/engine/code/tools/lcc/src/types.c create mode 100644 reaction/engine/code/ui/ui_atoms.c create mode 100644 reaction/engine/code/ui/ui_gameinfo.c create mode 100644 reaction/engine/code/ui/ui_local.h create mode 100644 reaction/engine/code/ui/ui_main.c create mode 100644 reaction/engine/code/ui/ui_players.c create mode 100644 reaction/engine/code/ui/ui_public.h create mode 100644 reaction/engine/code/ui/ui_shared.c create mode 100644 reaction/engine/code/ui/ui_shared.h create mode 100644 reaction/engine/code/ui/ui_syscalls.asm create mode 100644 reaction/engine/code/ui/ui_syscalls.c create mode 100755 reaction/engine/cross-make-mingw.sh create mode 100644 reaction/engine/id-readme.txt create mode 100755 reaction/engine/make-macosx-ub.sh create mode 100644 reaction/engine/md4-readme.txt create mode 100644 reaction/engine/misc/ReadMe-OSX.rtf create mode 100644 reaction/engine/misc/ioquake3-folder.icns create mode 100644 reaction/engine/misc/msvc/cgame.def create mode 100644 reaction/engine/misc/msvc/cgame.vcproj create mode 100644 reaction/engine/misc/msvc/game.def create mode 100644 reaction/engine/misc/msvc/game.vcproj create mode 100755 reaction/engine/misc/msvc/ioq3.sln create mode 100644 reaction/engine/misc/msvc/q3_ui.def create mode 100644 reaction/engine/misc/msvc/q3_ui.vcproj create mode 100644 reaction/engine/misc/msvc/quake3.vcproj create mode 100644 reaction/engine/misc/msvc/ui.def create mode 100644 reaction/engine/misc/msvc/ui.vcproj create mode 100644 reaction/engine/misc/nsis/Makefile create mode 100644 reaction/engine/misc/nsis/ioquake3-q3a.nsi create mode 100644 reaction/engine/misc/nsis/ioquake3-q3ctc.nsi create mode 100644 reaction/engine/misc/nsis/ioquake3.nsi.in create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/Controller.h create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/Controller.m create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/English.lproj/ErrorWindow.xib create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/English.lproj/InfoPlist.strings create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/English.lproj/Localizable.strings create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/English.lproj/MainMenu.xib create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/ErrorWindow.h create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/ErrorWindow.m create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/ErrorWindowController.h create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/ErrorWindowController.m create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/Info.plist create mode 100755 reaction/engine/misc/osxfe/ioquake3fe/ioquake3.icns create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/ioquake3fe.xcodeproj/TemplateIcon.icns create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/ioquake3fe.xcodeproj/bw.mode1v3 create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/ioquake3fe.xcodeproj/bw.pbxuser create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/ioquake3fe.xcodeproj/project.pbxproj create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/ioquake3fe_Prefix.pch create mode 100644 reaction/engine/misc/osxfe/ioquake3fe/main.m create mode 100644 reaction/engine/misc/quake3-tango.png create mode 100644 reaction/engine/misc/quake3-tango.xcf create mode 100644 reaction/engine/misc/quake3.icns create mode 100644 reaction/engine/misc/quake3.ico create mode 100644 reaction/engine/misc/quake3.ico-old create mode 100644 reaction/engine/misc/quake3.png create mode 100644 reaction/engine/misc/quake3.svg create mode 100644 reaction/engine/misc/sdl-win32-fixes.diff create mode 100755 reaction/engine/misc/setup/MacOSX/SLA-dmg.sh create mode 100644 reaction/engine/misc/setup/Makefile create mode 100644 reaction/engine/misc/setup/Solaris_pkg.sh create mode 100755 reaction/engine/misc/setup/doit create mode 100755 reaction/engine/misc/setup/install-desktop-files.sh create mode 100644 reaction/engine/misc/setup/ioq3demo.sh create mode 100644 reaction/engine/misc/setup/ioquake3.SlackBuild create mode 100644 reaction/engine/misc/setup/ioquake3.desktop create mode 100644 reaction/engine/misc/setup/ioquake3.sh create mode 100644 reaction/engine/misc/setup/pkg/ioq3ded.sh create mode 100644 reaction/engine/misc/setup/pkg/ioquake3.sh create mode 100644 reaction/engine/misc/setup/pkg/ioquake3/depend create mode 100644 reaction/engine/misc/setup/pkg/ioquake3/pkginfo.template create mode 100644 reaction/engine/misc/setup/pkg/ioquake3/postinstall create mode 100644 reaction/engine/misc/setup/pkg/ioquake3/postremove create mode 100644 reaction/engine/misc/setup/pkg/ioquake3/preinstall create mode 100644 reaction/engine/misc/setup/pkg/ioquake3/preremove create mode 100644 reaction/engine/misc/setup/pkg/ioquake3/prototype.template create mode 100644 reaction/engine/misc/setup/pkg/ioquake3/space create mode 100644 reaction/engine/misc/setup/pkg/ioquake3d/depend create mode 100644 reaction/engine/misc/setup/pkg/ioquake3d/pkginfo.template create mode 100644 reaction/engine/misc/setup/pkg/ioquake3d/postinstall create mode 100644 reaction/engine/misc/setup/pkg/ioquake3d/postremove create mode 100644 reaction/engine/misc/setup/pkg/ioquake3d/preinstall create mode 100644 reaction/engine/misc/setup/pkg/ioquake3d/preremove create mode 100644 reaction/engine/misc/setup/pkg/ioquake3d/prototype.template create mode 100644 reaction/engine/misc/setup/pkg/ioquake3d/space create mode 100644 reaction/engine/misc/setup/pkg/ioquake3m/depend create mode 100644 reaction/engine/misc/setup/pkg/ioquake3m/pkginfo.template create mode 100644 reaction/engine/misc/setup/pkg/ioquake3m/postinstall create mode 100644 reaction/engine/misc/setup/pkg/ioquake3m/postremove create mode 100644 reaction/engine/misc/setup/pkg/ioquake3m/preinstall create mode 100644 reaction/engine/misc/setup/pkg/ioquake3m/preremove create mode 100644 reaction/engine/misc/setup/pkg/ioquake3m/prototype.template create mode 100644 reaction/engine/misc/setup/pkg/ioquake3m/space create mode 100755 reaction/engine/misc/setup/preuninstall.sh create mode 100644 reaction/engine/misc/setup/setup.xml.in create mode 100644 reaction/engine/misc/setup/setup.xml.mod create mode 100644 reaction/engine/misc/setup/slack-desc create mode 100644 reaction/engine/misc/setup/splash.xpm create mode 100644 reaction/engine/ui/hud.txt create mode 100644 reaction/engine/ui/hud2.txt create mode 100644 reaction/engine/ui/ingame.txt create mode 100644 reaction/engine/ui/menudef.h create mode 100644 reaction/engine/ui/menus.txt create mode 100644 reaction/engine/voip-readme.txt create mode 100644 reaction/enginediff diff --git a/reaction/engine/.svnignore b/reaction/engine/.svnignore new file mode 100644 index 00000000..810b319c --- /dev/null +++ b/reaction/engine/.svnignore @@ -0,0 +1,2 @@ +build +Makefile.local diff --git a/reaction/engine/BUGS b/reaction/engine/BUGS new file mode 100644 index 00000000..081c55dc --- /dev/null +++ b/reaction/engine/BUGS @@ -0,0 +1,4 @@ +- On Solaris/SPARC gcc optimizations higher than -O0 currently lead + to a segfault + +https://bugzilla.icculus.org/ for more. diff --git a/reaction/engine/COPYING.txt b/reaction/engine/COPYING.txt new file mode 100644 index 00000000..98443f35 --- /dev/null +++ b/reaction/engine/COPYING.txt @@ -0,0 +1,281 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + END OF TERMS AND CONDITIONS + diff --git a/reaction/engine/ChangeLog b/reaction/engine/ChangeLog new file mode 100644 index 00000000..d8f866b1 --- /dev/null +++ b/reaction/engine/ChangeLog @@ -0,0 +1,3150 @@ +2008-04-04 Various contributors + + Solaris fixes + + Replace vsprintf function in bg_lib.c with vsnprintf implementation started by Patrick Powell + + Compile bg_* files separately for each game module, as originally intended + + Write q3config_server.cfg for the server, to avoid reseting client variables + after running a dedicated server + + Split image decoders into their own files + + OS X build updates for most compatibility + + Slackbuild + + Detect available resolutions and offer them in the in game menus + + A few botlib fixes + + Fix poppy captured audio when recording videos + + Extend console logging on crash errors + + Merge *BSD platform definitions in q_platform.h + + IRIX support + + Remove all the old bat/sh QVM building scripts + + Make master server used client configurable (cl_master) + + Fix to QVM compilation on big endian architectures + + OpenBSD support + + Autocomplete key names + + Don't build client command completion on the dedicated server + + Don't apply colour escape chars on input fields + + Rewrite of the win32 dedicated console + + Improved Makefile startup time + + Build dedicated server binary on Windows + + Bump Q3_VERSION to 1.35 + + Replacement of platform specific backends with a generic SDL one + + Merge win_net.c and unix_net.c to net_ip.c + + Demote input related console information to developer only so that it doesn't + spam the console every time input settings are changed + + PNG texture support + + Cleanup of tabulation in R_LoadImage + + Fixes to console scrolling + + New x86_64 vm that doesn't use gas + + Early out AABB collision optimisation + + Generate QVM dependicies in a better way + + Build process is quieter + + Replace horrendously long list of Makefile build rules with set of inference rules + + Allow CC to be overridden externally to the Makefile + + Move storage of console history from a cvar to a file in order to alleviate + security concerns + + Fix bug where transparent surfaces wouldn't draw over skyboxes + + Add input sanitising to various sound playing functions called from mods + + Explicitly set OpenAL distance attenuation model + + Increased the number of registers used for the opStack in the PPC vm from + 12 to 16 + + Fix endian issue in MDR loading + + Add cURL support for HTTP/FTP downloading + + Disable video command when not playing back a demo + + Print the SVN version string in Com_Init() + + OpenAL device enumeration support + + Fix 100% CPU usage on idle dedicated servers + + Windows home directory support + + Improve correctness of AVI files created by video command + + Better SDL joystick support + + sv_minRate + + [cl|sv]_packetdelay + + Various security fixes + + Fix JIT compiler code execution on NX-protected win32 OS + + Fix r_overBrightBits variable getting ignored on Linux + + cl_guid for semi-reliable server authentication + + Anisotropic texture filtering + + Video export doesn't crap out with sv_pure 1 anymore + + Video export doesn't crap out when writing > 2Gb files anymore + + Fix to a bug where servers with long uptimes (~27 days) would consume 100% + CPU if the running game did not set the nextmap cvar + + Some OSes no longer requires a vid_restart when changing r_fullscreen + +2006-01-24 Various contributors + + Persistent console history + + Added code to sleep a bit when q3 has no focus and sleep a lot when it's + minimised (SDL only) + + Cull excess speaker entities when using OpenAL + + Fix the operation of the delete key in *nix + + Only check the checksum on baseq3 pak0.pk3 + + Overhaul of console autocompletion + - No longer does weird stuff like move the cursor inappropriately + - Autocomplete works with compound commands + - Special autocomplete on some commands e.g. \map, \demo + - Removed various hacks used to counter the original autocomplete code + + Fixed the ability to disable Ogg Vorbis + + s/i686/i586/ - see bug #2578 + + Some sloppily coded mods call the Q3 sound API with NaNs -- sanitise this + + Removed advertising clause from BSD license as per mailing list discussion + + "make distclean" now does what you'd expect + + "make clean toolsclean" now does what "make distclean" did before + + GPL MD4 implementation + +2006-01-16 Various contributors + + Move code/unix/Makefile to ./Makefile + + x86 OS X support + + "quake3" shell script as shipped with 1.32 (on linux) no longer needed + + Ogg codec support from Joerg Dietrich + + Fix to the gcc4/-O0 x86 JIT compiler bug + + Up the defaults for zone and hunk memory since some mods (UT) have large + memory requirements that will have increased versus 1.32b due to some of the + alignment fixes + + Dependency generation for the .asm files + + Remove FS_SetRestrictions + + Add FS_CheckPak0 for better error messages where dumb users are involved + + Added cl_autoRecordDemo, which when enabled automatically records a new demo + on each map change + + Only display the g_synchronousClients warning when it's appropriate + + Remove custom memcpy/memset code + + AVI video output + - Uses motion jpeg codec by default + - Use cl_aviFrameRate to set a framerate + - \video [filename] to start capture + - \stopvideo to stop capture + - Audio capture is a bit ropey + + General Makefile improvements + + Support for MinGW cross compilation + + NetBSD support from optical + + x86_64 JIT bytecode compiler no longer disabled by default + + msvc project files updated and moved to win32/msvc + + Various alignment fixes + + Solaris (x86 and sparc) support from Vincent S. Cojot + + Fixed Altivec-based mesh rendering + + Ditch Mac OS 9 support + + Added a Makefile option USE_LOCAL_HEADERS which can be disabled to use system + headers if desired + + Detection of Altivec on Mac OS X + + SMP support with sdl_glimp.c on Mac OS X. + + Add "very high quality" option (patch from Pascal de Bruijn) + + Support for RIFF files with zero length data chunks (yes they exist, and yes, + they're legal) + + Support for ccache. If you want it, add USE_CCACHE=1 to Makefile.local + + Mac OS X now uses SDL backend, all Objective C removed + + Partial implementation of FS_Seek for files in pk3s + + Implementation of r_dlightBacks from Shane Isley + + OpenAL support, from BlackAura aka Stuart Dalton + + An abstract codec system, simplifying support for new formats + + Ignore in_dgamouse setting if dga isn't available + + Removed hard coded mouse acceleration in *nix input code + + Basically rewrote the lcc Makefile to be more sane + + Removed various bits of lcc that weren't built/needed + + General portability improvements + + Various variables added that aid packaging, from vapier + + Centralise architecture defines in q_platform.h + + Replaced a bunch of inline and __inline with ID_INLINE + + Replaced a bunch of __i386__ with id386 + + General tidy up of asm preprocessor decisions + + Removed C_ONLY from the dedicated server build + + Removed rule to build C++ (for splines) from the Makefile + + General decrufting + + Split USE_SDL into USE_SDL_VIDEO and USE_SDL_AUDIO + + Various assorted bug fixes + +2005-10-29 Various contributors + + nasm syntax asm ported to gas + + Disabled-by-default MD4 support + + cons build system removed + + Better FreeBSD support + + Makefile generates dependencies + + Some SDL sound tweaks + + qvm build tools and qvms are now built with the rest of the binaries + + q3asm-turbo from Phaethon + + Moved various displaced c and h files into more appropriate places + + A shitload (can I say shit?) of bug fixes -- see the svn log for details + +2005-09-22 Tim Angus + + MinGW port + +2005-09-?? + + SDL Stuff (icculus) + + x86_64 (ludwig von angstenheimer) + + patches from a cast of thousands + +2004-05-22 Timothee Besset + + updated the xcode project from Apple's version + now with the latest vm_ppc code + +2004-05-21 Timothee Besset + + fixed the Linux build to compile again on sid (glext.h and gcc3 warnings) + + 2 weeks ago, hacked up the source to compile on panther / xcode 1.1 + several cleanups were needed, and VM support seems broke (hangs or crashes) + + got altivec optimisations from Apple (Kenneth Dyke) + merged back in + + looks like with the new code merge the VM support is back in and working + +2003-09-15 Timothee Besset + + import Q3 java master code, cleanups on monster + +2003-08-31 Timothee Besset + + loki_setup hell + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=626 + http://zerowing.idsoftware.com/linux/q3a/index.html#glibc + text mode installer in loki_setup image built on Mandrake 7.2 crashes on + some glibc 2.3 systems such as RH9 etc. need to move to a different + version of the installer, and update old installers to keep them still + 'installing' moving to build the setup binaries on Debian Woody systems + (glibc 2.2, text mode installer will no longer work on 2.1 systems) hacked + together a new setup, using setup tree from RTCW. would need a complete + revamp if a new full setup with new binaries is needed + +2003-07-17 Timothee Besset + + new cvsreport, testing per-module config + +2003-01-19 Timothee Besset + + building on both gcc 2.x and 3.x + added conf modules to check gcc version + ccache support + +2003-01-13 Timothee Besset + + tweaking around for gcc 3.x build + edit Conscript to change the compiler + +2002-12-16 Timothee Besset + + added pbEmit class to auth code, emit CD keys to local PB master + +2002-11-14 Timothee Besset + + up to latest makeself.sh + + add both quake3.x86 and quake3-smp.x86 to setup + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=573 + console setup crash / glibc 2.3 (Debian Sid) + investigated, put together a workaround + +2002-11-5 Timothee Besset + + Linux building both smp and non-smp again. Will have to put both in setup + + added in_subframe to toggle X subframe event handling + + reworked the timing code to be more reliable + + cleaned up dgamouse/in_mouse code, removed unnecessary dgamouse var + + made the mouse grabbing an in_nograb cvar, no longer a compile time option + in_nograb 1 force in_dgamouse 0 and r_fullscreen 0 (any of those two will b0rk) + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=565 + mouse issues on Suze 8.1 - related to subframe event timing + added code chunk to detect broken X timing and disable subframe + + tweaked the subframe/X bug workaround to be less paranoid + +2002-10-28 Timothee Besset + + no longer blocking demo recording if g_synchronous clients != 0 + only sending out a warning (everyone does g_sync 1 ; record ; g_sync 0) + +2002-10-21 Timothee Besset + + building final mod sdk setups (added lcc bins, added link to q3asm-turbo in readme) + +2002-10-8 Timothee Besset + + quickfix cl_maxpackets > 125 brings back to 100 + +1.32 release --- + +2002-10-7 Timothee Besset + + made the 'demo' command case-insensitive on extension match (it was confused by demo FOUR.DM_68) + + mouse wheel scrolling with in_mouse 1 + window mode was not working, fixed (DI didn't catch) + + removing on-the-fly pk3 build from Linux setup, using the finalized ones now + added 'pk3' option to cons for toggle of pk3 building + +2002-10-5 Timothee Besset + + updated win32 mod sdk (in win32/mod-sdk-setup) + added q3asm and lcc source + updated the .bat to build VMs + +2002-10-3 Timothee Besset + + linux mod sdk, wrote the bulk of the scripts + +2002-9-30 Timothee Besset + + ATVI Quake 3 1.32 Patch #9 + rolling back to the way it was before, leaving 1v1 force vote exploit, the fix was worse than the bug + from comment on bug #9 in tracker: + + actually the fix is worse than the original bug + + after the fix, voting when you are alone on the server was no longer working + it was kinda intended in the fix, that you would have to be at least two to pass a vote .. but + it is an oversight. + + calling a vote in a 1v1 game against a bot fails immediately + (calling a vote in any situation where there's only 1 live player fails) + + Say a server's running some lame custom map that you have but a friend doesn't. You can't go + on the server and change it to the map you want to play, so he ends up having to auto-dl it at + 8K a second just so you can switch from it. + + This particular 2 clients, vote / quit exploit would involve too many changes to fix properly. + I am reverting back to the old version, and leaving as WNF + +2002-9-29 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=50 + added Wheel support to the DirectInput code IN_DIMouse (in_mouse 1) + tweaked the Wheel mouse reading for in_mouse -1 (old win32 input code) + handle correctly when zDelta is > 120 + provide a in_logitechbug cvar to handle buggy Logitech MouseWare driver sending wheel events twice + +2002-9-26 Timothee Besset + + ATVI Quake 3 1.32 Patch #38 + adding trap_SetPbClStatus, reliably checks for PB presence before enabling PB in UI + +2002-9-25 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=551 + SVF_CLIENTMASK, fixed a typo + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=555 + pushed cl_maxpackets upper limit to 125 (from 100) per CPMA Arqon's request + +2002-9-24 Timothee Besset + + ATVI Quake 3 1.32 Patch #33 + PB reporting sv_paused cvar hacked, fixed SV_CheckPaused to use a Cvar_Set + + ATVI Quake 3 1.32 Patch #24 + added [skipnotify] from RTCW, use to display in the console only, but not on client screen + (also fixes pb_msgprefix and pb_sv_msgprefix) + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=553 + using correct error message if listen server starting as cl_punkbuster 0 sv_punkbuster 1 + + ATVI Quake 3 1.32 Patch #35 + text auto wrap in UI code was eating the last word if it was wrapping + fixed in Q3 and TA UI (this bug could have affected the server print message also) + + some updates to the win32 cons post-build process + +2002-9-21 Timothee Besset + + adding bspc cons build script + +2002-9-19 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=552 + disconnect reason is transmitted in the disconnect command and processed into com_errorMessage + (similar to RTCW behaviour) + added UI for com_errorMessage cvar in baseq3/, if client is kicked/dropped/disconnected for whatever reason + (this is already functional in TA) + + ATVI Quake 3 1.32 Patch #9 + failing vote if there's only one voting client (fixes exploit on 2-player server where one player votes then disconnects, forcing vote to pass) + + ATVI Quake 3 1.32 Patch #5 + removed the userInfoChanged message (was a debugging leftover) + + ATVI Quake 3 1.32 Patch #18 + rcon was not properly fixed yet, this only showed up for PB commands + changed the rcon parsing again to be more reliable + +2002-9-18 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=549 + the demo command has a list of compatible protocols, it will loop through 66 67 68 + you can do '/demo four' and it will try four.dm_66 four.dm_67 four.dm_68 + or you can explicitely give a '/demo demoname.dm_??' + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=551 + added SVF_CLIENTMASK (0x00000002), works only with <= 32 players + set bitmask of players to which send entity + +2002-9-17 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=550 + rcon bug fix + + some scons updates for win32 (post build) + + 1.32rc2 + +2002-9-06 Timothee Besset + + updated completely the setup system: + fixed cons stuff to build setup with cons -- release setup + working from new setup codebase with some custom patches: + https://bugzilla.icculus.org/show_bug.cgi?id=52 + https://bugzilla.icculus.org/show_bug.cgi?id=53 + checked that BSD support was still in (brandelfing and symlinks) .. will have to get tester feedback + bumped version to 1.32rc1 + TODO: update the windows .VCT (standalone setup and auto-update) + +2002-9-04 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543 + backport from RTCW 1.4 code + rcon commands where sent after being tokenized and rebuilt + that was breaking any quoting, for instance 'rcon g_motd "hooka pooka"' + added Cmd_Cmd() to retrieve the un-tokenized command and transmit as is on both ends + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=542 + b0rked text wrapping in connect screen + was a missing sizeScale in q3_ui/, and a bad param in ui/ + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=540 + backport fix to pk3 reordering, happens when clearing the references, bad order from connection may break stuff + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=527 + TA ui/, quickfix to netSource (mod stuff, doesn't affect TA) + + cleaned up broken old DO_WIN32 stuff in cons scripts + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=526 + typo in models2.shader + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=443 + Linux client: sub-frame timing of key/mouse events + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=453 + added mousewheel support: wheel to scroll, ctrl+wheel to scroll faster, shift+wheel to scroll history + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=545 + bumped server count to 4096 + + keep around: __asm__ __volatile__ ("int $0x03"); + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=516 + moved screenshots to backend with a new RC_SCREENSHOT render command + fixes the r_smp 1 garbled screenshots + +2002-8-29 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=539 + new VM code from Raven's Sof2 + + cons / qvmtools build system fixes + + had to get a new qe3.ico again (resource compiler error) + http://vasin.hypermart.net/eei.htm + + updated, basic testing on win32, merging back in trunk + + merged bug-539 branch back into trunk, officialize the new VM code + +2002-8-26 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=472 + linux client: handle ctrl+space situations (could leave space locked on + space not working with ctrl on) + + update the build system, build q3lcc and q3asm etc. on demand + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=62 + fixed invisible players/entities + +2002-8-23 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=536 + fixing donedl being ignored after autodl if map_restart'ed (propagate from RTCW) + ignoring multiple map_restart (propagated from RTCW) + + reworked the server 'client text ignored' message to only trigger when there's actually a message that doesn't get to the game VM + +2002-8-18 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=528 + ydnar: reorg bits in the drawsurf sort index, push MAX_SHADERS to 2^12 + + commented out some debug stuff in java auth server + + added FAQ item with Linux & BSD patch to handle broadcast on multiple interfaces + +2002-8-15 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=534 + fixing rcon being broken on NT/XP with > 23 days uptime (or so) + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=525 + changed the rcon buffer size to avoid overflows and dropping part of the message + +2002-8-14 Timothee Besset + + hacked in some experimental win32 stuff to the cons files + (win32 recognition and pk3 installs .. very very experimental but I needed it for win32 dev) + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=521 + ui/ and q3_ui/ : added text auto wrapping in the connection screen drawing (server message) + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=531 + removed the MPlayer stuff from the server browsers + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=505 + enabled back the ignore if protocol is != (fixes Wolf servers showing in browser) + +2002-8-10 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=500 + propagated IP banning fix from RTCW + +2002-8-08 Timothee Besset + + propagate additional sv_lanForceRate fix from RTCW + +2002-8-07 Timothee Besset + + added trap_FS_Seek + +2002-8-05 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=50 + fixed the DI mouse init procedure + +2002-8-05 Timothee Besset + + removed sv_allowanonymous, was dummy and polluting the serverinfo + (sv_allowanonymous was designed to flag wether server was public or not, but that's replaced by g_needpass) + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=514 + sv_strictAuth (default 1): server variable to control wether strict CDKEY auth should be performed + this is required if you want reliable cl_guid on the server + extended the getIpAuthorize (server->auth message) syntax + sending the fs_game at all times (default 'baseq3'), dummy sv_allowAnonymous 0, strict auth flag + NOTE: 1.31 server on baseq3 sends a getIpAuthorize packet like: + processing packet: getIpAuthorize -1230824753 217.128.77.195 0 + the auth server will mistakenly read fs_game as '0' + + TAGGED the master / auth source as pre-1_32 + will need to go back to this to comment out all my debugging crap + +2002-8-04 Timothee Besset + + cleaned master server stuff, client was prompting master.quake3arena.com, + server was sending heartbeats to master3.idsoftware.com + both point to 192.246.40.56, unified to master.quake3arena.com + the MPlayer master, master.quake3world.com doesn't exist anymore, switched it to master.quake3arena.com + +2002-8-02 Timothee Besset + + added auth server source, reorganized + + auth server name / master key optionally set on command line for master and auth servers + + auth and master config in build system + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=524 + changed default GL driver from libGL.so to libGL.so.1 + see LSB 1.2 spec: http://www.linuxbase.org/spec/refspecs/LSB_1.2.0/gLSB/libgl.html + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=480 + applying the 'no cp command' experimental fix for beta phase + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=462 + backported from RTCW, fix to packet fragmenting emission + FIXME: there is some verbose code that we have to take out in the final version (grep for #462) + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475 + backported from RTCW, don't get dropped if the server changes map while connecting (ignore outdated cp) + + PROTOCOL BUMPED TO 68 + +2002-8-01 Timothee Besset + + Linux: dedicated build was not setting up signal handler like the full client does + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=522 + SplashDamage bugfix, now clearing client gentity before GAME_INIT call (instead of after) + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=498 + fixed NET_AdrToString to print the port as unsigned int (for ports > 1^^15, was showing negative) + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=501 + maintain IP in userinfo sent to game + + checking in master server source + +2002-7-31 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=513 + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=506 + porting fix from RTCW codebase. client re-orders it's pk3s to scan in the same order than the server + this eliminates several 'Invalid .PK3 file referenced' situations (caused by client not referencing the same thing as server) + + fixed border remnants in ta ui + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=517 + ERR_DROP if PB client off / server on conflict when starting local server + + quickfix to q3 ui / punkbuster detect in server browser + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=458 + code fix, no more taunt spam + + cons install of PB .so files + + correct MOD_KAMIKAZE and MOD_JUICED in TA games.log + +2002-7-29 Timothee Besset + + q3 ui: completed confirmation prompts and messages (added UI_ConfirmMenu_Style & UI_Message) + + ta ui: backported 'conditionalopen' from RTCW (conditionalopen ) + + ta ui: confirmation prompt for punkbuster enable/disable etc. + + added the win32 DLLs to pb/win32/ + +2002-7-28 Timothee Besset + + ta ui: sv_punkbuster in StartServer menu + + ta ui: added cl_punkbuster in server browser + + view filters are in a modal dialog + + new files: filter.menu menus.txt (pak3.pk3 updated) + + fix broken link in Linux FAQ + +2002-7-27 Timothee Besset + + ta ui: PB display in the browser, in it's additional tab, with sorting + +2002-7-26 Timothee Besset + + PB UI: for baseq3/ AND missionpack/ + q3_ui: Punkbuster: Enable/Disable in server broswer (cl_punkbuster) + q3_ui: PB logo, PB Yes/No in browser (TODO: validate this to be working) + q3_ui: added sv_punkbuster toggle in start server menu + + automated building of the new PK3s, unix/Conscript-pk3 + +2002-7-25 Timothee Besset + + added PB build scripts on Linux, fixed the Linux build + +2002-7-12 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=511 + fixing re.SetColor crash for widescreen displays (q3dm11) + was calling to the renderer while not registered + +2002-6-19 Timothee Besset + + r_roundImagesDown 0 + map q3dm16 -> tr_image.c ResampleTexture crash + buffer overflow because of resample to 2048x.. + xian_q3dm12_leftwall4fin.jpg 1152x384 + bumped one buffer byte p1[1024] -> byte p1[2048], added a safe check + +2002-6-14 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493 + propagate a renderer fix from RTCW. fixes a one-frame visual glitch when mod code + registers a shader after drawsurfaces are generated but before the frame is rendered + +2002-6-12 Timothee Besset + + added cons and pcons to unix/, updated the build script + +2002-5-24 Timothee Besset + towards a new Q3 release? + some bug fixes require protocol change, or mod code/mod interface change to be fixed properly + this is a biz decision, dunno yet if we are going to want a new protocol (probably not) + -> have to create a branch for the 1.31b, i.e. backwards compatible with 1.31 'Stable-1_31' + and put the 1.32 specific / protocol changes on trunk + no telling what will go in SOS in the end .. probably 1.32 + +2002-5-5 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=491 + adding a sv_lanForceRate (defaults to 1) to turn on/off server forcing rate of LAN clients + (only affects LAN dedicated clients - dedicated 1, default behaviour forces LAN clients to 99999 rate) + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=470 + fixing potential overflows with cl_cdkey (propagated from RTCW) + + cons-based build system (imported from Wolf, was partly written for mod tools release already) + building with SMP on by default + + better #ifdef SMP handling ('disabled at compile time' message) + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=494 + Q_vsnprintf for vsprintf calls in the core + not putting this in game code as we'd need a vsnprintf implementation in bg_lib.c + +2002-4-5 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=462 + taking out the fix which was found broken and incomplete + +2002-8-4 Timothee Besset + + adding NO_MOUSEGRAB define (select in the Makefile) + +2002-2-4 Timothee Besset + + applying Gareth's SMP patch + + count number of CPUs (Sys_ProcessorCount in unix_shared.c), default r_smp appropriately + + bumping version to 1.32 + + if XInitThreads fails, set r_smp to zero + +2002-28-2 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=462 + send potential remaining fragmented packets before sending a gamestate + +2002-26-2 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=455 + removed old libMesaVoodooGL.so loading code + Voodoo cards should use XF4/DRI, that load code was outdated and confusing people with broken OpenGL + +2002-16-1 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=441 + adding brandelf calls to the setup building process so that our binaries run on BSD + +2002-1-1 Timothee Besset + + updated FAQ with BSD info (bug #441) + + FAQ update on CLIENT_UNKNOWN_TO_AUTH + + FAQ update for proper strace usage + +2001-12-12 Timothee Besset + + Q3 1.31 point release + updating build_setup.sh to new pk3 files + (baseq3/pak7.pk3 missionpack/pak2.pk3) + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=395 + adding quake3.xpm icon, and modified the setup accordingly to put symlinks + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=390 + ignoring SIGTTIN SIGTTOU + +2001-06-12 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=402 + bug with full scene + +2001-04-12 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=398 + cg_bobup cheat protect + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=399 + fixed Setup > System > Driver info crash + + checked in code/spank.sh script, perform checksuming + +2001-18-09 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371 + propagating sound code fixes from Wolf to Q3 + +2001-11-08 Timothee Besset + + setup script was still broken, damn shell expansion + the exit code for Q3 was always zero instead of $? + propagating the fix to Wolf + +2001-11-04 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=382 + modified challenge code for motd to be truly random + +2001-10-31 Timothee Besset + Moved updated q3asm and lcc source at the toplevel, MissionPack/q3asm + and MissionPack/lcc + +2001-10-29 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=381 + build system is now functional + +2001-10-21 Timothee Besset + + updated Sys_LoadDll code on linux to search in the following order: + #1 current directory + #2 fs_homepath + #3 fs_basepath + this was needed to make mod developement easier + +2001-10-09 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=51 + the code to buffer the redirection was in there but disabled? (Com_Printf) + enabled it back + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=52 + connection issues / userinfo + client side fix, instead of sending 'connect ' packet + we now send 'connect ""' + +2001-10-08 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371 + added a PROT_READ to the mmap call + this was needed to go around a bug in glibc i586 i686, memset doing read access + since the audio_fd is opened O_RDWD this is harmless to Q3 + +2001-10-07 Timothee Besset + + updating from SOS + S_WriteLinearBlastStereo16 C/asm is back in snd_mix.c (Graeme) + r_showtris r_shownormal cheat protections + + Sys_LoadDll changes: + removing -debug search when loading native dlls + changing the fatal aborts when not finding native from release only to debug only (was a misfeature) + used to search in cd_path which is bogus, now searching in pwd if basepath fails + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=275 + fixed r_fullbright not being cheat protected / was a CVAR_LATCHED|CVAR_CHEAT issue + +2001-09-06 Timothee Besset + + updated from SOS, some changes to qcommon/unzip.c (statics) + +2001-08-27 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=3 + Added some code in CL_InitDownloads to use FS_ComparePaks and print out information about server-referenced paks that are missing on the client. It is a first step, allows to get precise information about what can cause a connection to fail (typically when the user is sent back to the main screen). + +2001-08-22 Timothee Besset + + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=86 + fixed sound bug (with Graeme hints) + +2001-08-20 Timothee Besset + + made sure Sys_Printf doesn't get into an endless loop if logfile is on + fixed qconsole.log issues, +set logfile 1 +set fs_debug 1 was crashing (any OS) + fixed logfile 1 / ttycon 1 issue, didn't exit properly (same endless looping) + also fixes an issue reported by q3f team + + changed rcon commands from Com_DPrintf to Com_Printf so that they show up in the console + (with IP information) + +2001-08-19 Timothee Besset + + fixed https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=91 + (autodownload toggle in q3 ui) + + fixed https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=76 + g_password issue + + fixed https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=93 + cheat protecting r_lodCurveError + + wontfix https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=92 + +2001-08-18 Timothee Besset + + more fixes to the 7-button mouse code (linux only) + + updated faq about gamma slider + + added "servers don't show up in ingame browser" to faq + + added Alt+Enter toggle for fullscreen/windowed (linux) + +2001-08-16 Timothee Besset + reconfiguring CVS repository to give access to Gareth + + testin gareth's access + +2001-08-03 Timothee Besset + * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=85 + fixes in the setup code for older bash versions + +2001-08-02 Timothee Besset + * commented out assembly implementation of S_WriteLinearBlastStereo16, using modified C implementation from Zaphod + need to check performance: https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=88 + * finished const declarations in CG_Trace calls, was needed in pmove_t declaration and some other functions + cgame/cg_local.h : CG_trace trap_CM_BoxTrace + game/bg_public.h : using const in pmove_t trace functions prototypes + (fixes gcc warnings: assignment from incompatible pointer type) + +2001-07-26 Timothee Besset + * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=78 + mapped K_MOUSE4 K_MOUSE5 + +2001-07-23 Timothee Besset + * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=5 + more fixes, handling meta characters and various kinds of backspace + +2001-07-22 Timothee Besset + * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=5 + after testing feedback, fixed more stuff: + better backspace, works with putty and potentially more terminals + * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=51 + band aid fix to rcon status, incresed MAX_PUSHED_EVENTS from 256 to 1024 + (adds 28kb of mem requirements) + +2001-07-21 Timothee Besset + * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=2 + using XF86 Gamma extension to set the gamma in game from the menus + (previous behaviour was to set /r_gamma and restart, renderer relying on s_gammatable) + restoring initial gamma on GLimp_ShutDown + +2001-07-19 Timothee Besset + * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=5 + first usable version of dedicated console + added history and completion functionality + ready for some testing + still some TODOs and FIXMEs: + keep the currently edited line when going back from history exploration + edit the current line with cursor, insert mode etc. + +2001-07-18 Timothee Besset + * starting TAB completion and history for the dedicated server (tty console) + removed Sys_ConsoleOutput (unused) + removing bogus nostdout variable + cleanup of a big chunk of code that Bernd commented out and scheduled for deletion + moved completion code from client/cl_keys.c stuff into qcommon/common.c, Field_CompleteCommand(field_t*) + +2001-07-13 Timothee Besset + * fixed https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=8 + screenshots overwrites + * fixed https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=31 + DOUBLE SIGNAL FAULT + +2001-07-11 Timothee Besset + * fix for french keybards / console toggle / bound to XK_twosuperior + +2001-07-10 Timothee Besset + * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=19 + cleanup of the keyboard code, adding com_developer message in case XLookupString would fail + +2001-07-10 Timothee Besset + * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=33 + using our custom handlers for X errors, should make things more robust + (X docs say some X errors are not fatal, but the default X handler exits the app anyway) + +2001-07-08 Timothee Besset + * https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=19 + keyboard state issues, fixed the sticking with ctrl key (thks relnev) + +2001-07-07 Timothee Besset + * closing https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=13 + the fixes to bug #9 solved this one too + * checking in to SOS + +2001-07-05 Timothee Besset + * work on https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=9 + filesystem code changes: + updated the documentation in files.c to the current system + added correct fs_homepath fs_basepath fs_cdpath scanning to FS_SV_FOpenFileRead + (fixes description.txt not found, and probably a few other linux issues) + +2001-06-29 Timothee Besset + * fixed setup issues (graphical/console) + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=6 + +2001-06-26 Timothee Besset + * bug tracker is online at https://zerowing.idsoftware.com/bugzilla + authentication, use login: bugs password: b00gies + for now, using it as the linux bug tracker, possible use for more OSes and programs if anyone is interested. + * tweaked the graphical setup to send to bugs@idsoftware.com on errors instead of support@lokigames.com + +2001-06-19 Timothee Besset + * fixed generated launch script /usr/local/bin/quake3, exit $* should be exit + +2001-06-18 Timothee Besset + * rebuilt 1.29f setups, released as 1.29f-beta1 'Q3 1.29f linux-i386 Jun 19 2001' + +2001-06-10 Timothee Besset + * rebuilt against PR source, packaged 1.29b setups + +2001-05-25 Timothee Besset + * graphical setup, based on Loki's setup tool (GPL) + +2001-05-22 Timothee Besset + * changed fs_basepath to fs_homepath, according to Graeme's changes (probably missed this change?) + this fixes the q3key prompting at each game startup + +2001-05-20 Timothee Besset + * rebuilding 1.28b, various fixes on linux build: + - SetProgramPath was renamed to Sys_SetDefaultCDPath in unix_shared.c + updated unix_main.c accordingly + - some prototypes in qgl.h are guarded by #ifndef GL_VERSION_1_2 (ARB extentions) + those prototypes are needed by linux_glimp for importing functions and casting, added a #ifdef __linux__ + (not a clean solution) + - game/q_shared.h + little endian / big endian functions have been added + gcc generates warnings about functions being unused .. inlined them + - cgame/cg_marks.c + // TTimo + // gcc warning: might be used uninitialized + float sInc = 0.0; + float s = 0.0; + +2001-05-15 Timothee Besset + * fixes to linux Makefile for bspc 2.1h + * various updates to 1.28b on linux + +2001-05-09 Timothee Besset + + * R. Duffy reverted game/bg_pmove.c PM_CheckDuck, was a merging screup on my side + * updated setup to 1.27z, removed the .so from the setup distribution (they were in 1.27g because of issues) + FIXME: gotta get pk3's first + +2001-05-04 Timothee Besset + + * fixes to gcc, building RC for 1.27s + +2001-05-01 Timothee Besset + + * added qcommon/huffman.c to the Makefile + * gcc -Wall: + commenting out + CL_Netchan_Encode CL_Netchan_Decode (cl_net_chan.c) + Netchan_ScramblePacket Netchan_UnScramblePacket (net_chan.c) + SV_Netchan_Encode SV_Netchan_Decode (sv_net_chan.c) + +2001-04-26 Timothee Besset + + * fixed dedicated server crash when entering the VM_COMPILED qagame on a mod (some statics lacked initialization) + +2001-04-25 Timothee Besset + + * added $(Q3POBJ) to clean target (cleanup of platform-dependent objects) + * more make clean improvements + +2001-04-23 Timothee Besset + + * cleanup the mod selection code, remove duplicates + * some issues with release builds, my main developement box doesn't build stable binaries with release settings + removing -fomit-frame-pointer seems to fix (there's probably a performance hit) + see OMIT-FRAME-POINTER.txt + +2001-04-13 Timothee Besset + + * checked in a first set of merged files + +2001-04-06 Timothee Besset + + * merged back the core linux parts to make 1.27g linux build from the Source Safe tree again + +2001-02-27 Bernd Kreimeier + + * CVS: tag with changes as of today + cvs tag id1-27j-loki01027 + + * code/qcommon/msg.c: numFields loop (SOS). + * code/qcommon/files.c: ue Q_stricmp (SOS uses stricmp, was strcmp). + * code/game/q_shared.h (Q3_VERSION): 1.27j. Also + MAX_STRING_TOKENS upped from 256 to 1024 (SOS). + + * code/server/sv_snapshot.c (SV_AddEntitiesVisibleFromPoint): see below. + * code/game/g_public.h (SVF_NOTSINGLECLIENT): added (SOS). + + * code/server/sv_ccmds.c: see below. + * code/game/g_main.c: g_gametype cvar now userinfo (SOS). + + * code/game/g_active.c (SendPendingPredictableEvents): new (SOS). + * code/game/bg_misc.c: new SOS (sos010227) + + * SOS: new update sos010227. + +2001-02-22 Bernd Kreimeier + + * CVS: now in sync with last SOS and cleanup up + cvs tag id1-27i-loki01022 + + * code/ui/ui_shared.c: below. + * code/ui/ui_main.c: leftover code! + * code/server/sv_world.c: below. + * code/server/sv_snapshot.c: below. + * code/server/sv_init.c: below. + * code/server/sv_game.c: below. + * code/server/sv_client.c: below. + * code/server/sv_ccmds.c: below. + * code/server/sv_bot.c: below. + * code/server/server.h: below. + * code/renderer/tr_surface.c: below. + * code/renderer/tr_shader.c: changed assert to early return. + * code/renderer/tr_shade_calc.c: below. + * code/renderer/tr_shade.c: below. + * code/renderer/tr_scene.c: below. + * code/renderer/tr_mesh.c: below. + * code/renderer/tr_local.h: below. + * code/qcommon/vm_x86.c: cleanup. + * code/qcommon/vm.c: below. + * code/qcommon/unzip.c: below. + * code/qcommon/qcommon.h: below. + * code/qcommon/files.c: below. + * code/qcommon/cvar.c: cleanup. + +2001-02-21 Bernd Kreimeier + + * code/qcommon/common.c: cleanup. + * code/qcommon/cm_trace.c: cleanup. + * code/qcommon/cm_patch.c: cleanup. + * code/qcommon/cm_public.h: cleanup. + * code/game/q_shared.h: cleanup. + * code/game/q_shared.c: cleanup. + * code/game/q_math.c: cleanup. + * code/game/g_syscalls.asm: changed (once more) floor,ceil etc. + * code/game/g_spawn.c: cleanup. + * code/game/g_session.c: cleanup. + * code/game/g_cmds.c: cleanup. + * code/game/g_client.c: cleanup. + * code/game/g_arenas.c: cleanup. + * code/game/bg_slidemove.c: cleanup. + * code/game/bg_pmove.c (PM_CheckDuck): old call to trace? + * code/game/bg_misc.c: cleanup. + * code/game/be_aas.h: dead code. + * code/game/ai_dmq3.c: cleanup. One clear/copy switched? + * code/game/ai_dmnet.c: more //*/. Why oh why not DEBUG.... + + * code/client/snd_mix.c: below. + * code/client/snd_dma.c: below. + * code/client/keys.h: cleanup. + TODO: #error in q3_ui/keycodes.h ? + + * code/client/client.h: cleanup. + * code/client/cl_main.c: misplaced bracket. Cleanup. + * code/client/cl_keys.c: below. + * code/client/cl_cin.c: below. + * code/client/cl_cgame.c: cleanup. + TODO: define assert for Win32 or guard my assertions. + + * code/cgame/cg_syscalls.c: below. + * code/cgame/cg_servercmds.c: below. + * code/cgame/cg_players.c: cleanup. + + * code/cgame/cg_newdraw.c: remember to diff against cg_newDraw.c + in SOS (mixed case). + TODO: get id to use cg_newdraw.c, and to remove cg_newDraw.c/cpp. + + * code/cgame/cg_main.c: below. + * code/cgame/cg_local.h: below. + * code/cgame/cg_event.c: below. + * code/cgame/cg_drawtools.c: below. + * code/cgame/cg_draw.c: cleanup. + * code/cgame/cg_consolecmds.c: dead code. + * code/bspc/qbsp.h: below. + * code/bspc/l_poly.c: below. + * code/bspc/l_math.c: cleanup. + * code/bspc/bspc.c: cleanup. + * code/bspc/be_aas_bspc.c: cleanup. + * code/bspc/aas_map.c: kept comments - merge loss at their end? + * code/bspc/aas_file.c: cleanup. + + * code/botlib/be_interface.c: this file is plain impossible. There + are layers of code made dead with /* */ and the resurrected by + //* or // /* or variations of this. I reverted to exact mirror + image of SOS to be sure - short of removing it's too easy to mistake + live code for dead one. + Later: have to change 5 occurences to avoid gcc complaints about + nested comment tokens. + TODO: somebody please get rid of the cruft in here. + + * code/botlib/be_ai_move.c: redundant typedef. + * code/botlib/be_ai_chat.c: assertions on signed string index. + Note: this is not in my ChangeLog - ouch. + TODO: use gcc -fsigned-char on all platsforms to enforce Win32 + TODO behavior (PPC has a default unsigned char, Intel has not). + * code/botlib/be_aas_sample.c (AAS_TraceClientBBox): one code block + was placed in different location, and one FPE hack not used. I would + expect that divide by zero will still occur here. + + * code/botlib/be_aas_reach.c: below. + * code/botlib/be_aas_cluster.c: cleanup. + * CVS: the last tag (below) marks the version with a lot of history + and additional comments. I am now bringing the codebase in sync with + SOS as of yesterday, cleaning out comments, dead code and other + differences to minimize a diff - in a valiant if futile attempt to + roll back changes into the id codebase. + Note: I ignore the $SOS$ - these are unfortunate but will change + in the same awkward way at their end. + Note: I stick to #if 0 instead of C comments around dead code id + kept (nested comments issue). The commentary is changed to sosYYMMDD + and includes the token DEAD. + +2001-02-20 Bernd Kreimeier + + * CVS: update, then tag current version as + cvs tag id1-27i-loki010219 + + * SOS: patched up to sos010219. + + * code/qcommon/cm_trace.c (CM_Trace): fabs on sphere offsets (SOS). + * code/game/bg_slidemove.c (PM_StepSlideMove): stepSize vs. STEPSIZE (SOS). + * code/game/bg_pmove.c (PM_CheckDuck): fix in stand up check (SOS). + * code/bspc/bspc.c (main): -capsule (SOS). + * code/bspc/qbsp.h: below (SOS). + * code/bspc/be_aas_bspc.c (capsule_collision): added (SOS). + * code/bspc/aas_map.c (CapsuleOriginDistanceFromPlane): added and used (SOS). + * code/bspc/aas_file.c (AAS_WriteAASFile): removed diagnostics recently + added. No matter how long you wait, they'll always get you ;-). + * code/botlib/be_aas_cluster.c: enabled LogWrites, different flood (SOS). + + * SOS: patching up to snapshot sos010219. + Note: For brevity, I use as marker sosYYMMDD now instead of bkYYMMDD, to + distinguish from changes not in SOS. + + * CVS: tagged current version before patching up with SOS. + cvs tag id1-27i-loki010216-bsd + +2001-02-16 Bernd Kreimeier + + * code/server/sv_init.c: DLL_ONLY sets sv_pure to 0 and ROM. + TODO: determine good sv_pure policy for DLL-only servers. + + * code/renderer/tr_shade_calc.c: my_ftol implementation (BSD). + + * code/unix/Makefile: FreeBSD sections. + TODO: include target-specific Make-freebsd etc., + include a Make-local not in CVS for build preferences, + and generally clean up this mess. + * code/unix/unix_glw.h: guard #error + * code/unix/linux_snd.c: soundcard.h location (BSD). + * code/unix/linux_glimp.c: guard system headers. + Later: added Joystick stubs. + Note: linux_ etc. prefixes start to loose meaning as we + re-use most of this on UNIXes anyway. I didn't use Raf's + freebsd_joystick.c but instead put generic stubs here. + TODO: introduce generic -DNO_JOYSTICK flag. + * code/renderer/tr_local.h: my_ftol guard. + * code/renderer/qgl.h: FreeBSD guards. + * code/qcommon/vm_x86.c: sys/types include on FreeBSD. + * code/qcommon/md4.c: Win32 pragma guard. + * code/qcommon/common.c: Com_Memcpy/Memset external. + * code/game/q_shared.h: added FreeBSD defines. + * code/game/q_math.c (BoxOnPlaneSide): FreeBSD conditional. + TODO: check whether we have/need the assembly version anyway. + * code/client/snd_mix.c: use C fallback on FreeBSD. + Note: all of the above changes from the original port by Rafael Barrero. + + * CVS: tagged current version before merging FreeBSD related changes. + cvs tag id1-27i-loki010215-ppc + +2001-02-15 Bernd Kreimeier + + * code/unix/Makefile: BSD related changes. + * code/cgame/cg_draw.c: hacked phone jack rendering check for Debug. + TODO: finish Debug, fix CG_DrawDisconnect !!! + + * code/unix/vm_x86.c: error on compile attempts. Fight redundancy! + * code/qcommon/vm_x86.c (VM_CallCompiled): dummy for linkage on PPC. + Note: DLL_ONLY is the global Makefile option for DLL-only builts. + Currently only executed on Linux. + * code/unix/unix_main.c: *ppc postfix for DLLs. Ignored the changes + to redundant code (have to remove the unused Un/LoadDll/API calls). + * code/server/sv_game.c (VMA): changed macro (see below). PPC. + * code/qcommon/vm.c (VM_DllSyscall): see lengthy commentary by Ryan. + The existing VM code makes certain assumptions about the layout of + varargs on the stack, which fall apart with call conventions that + don't even put all parameters on the stack (gcc on PPC, register-rich). + Using a dedicated memory area as our own stack. This should actually + be the default behavior. + Later: make vm_* cvars INIT/ROM for DLL_ONLY target. + + * code/qcommon/common.c: PPC change (from Ryan Gordon). + +2001-02-07 Bernd Kreimeier + + * code/unix/unix_main.c: disabled FPE for debug for the time + being (that is, until I can figure out + Program received signal SIGFPE, Arithmetic exception. + RB_BeginSurface (shader=0x449572e0, fogNum=0) at ..//renderer/tr_shade.c:307 + 307 tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; + without any NaN's involved. + TODO: unmask other FPE's selectively (see Mike's Tribes2, no getenv though). + +2001-02-06 Bernd Kreimeier + + * SOS: up to date with todays snapshot. + Note: got the date wrong, comment used was bk010205. Duh. + + * code/server/sv_snapshot.c (SV_UpdateServerCommandsToClient): below. + * code/server/sv_main.c (SV_ReplacePendingServerCommands): new (SOS). + * code/server/server.h: reliableSent (SOS). + + * code/renderer/tr_shade.c (ProjectDlightTexture): see below. + * code/renderer/tr_scene.c: see below. + * code/renderer/tr_public.h: see below (SOS). + * code/renderer/tr_local.h: additive light support (SOS). + + * code/qcommon/cm_trace.c (CM_Trace): new tw.sphere.use branch (SOS). + + * code/game/g_spawn.c: notta, notq3a entities (SOS). + * code/game/ai_dmq3.c: MAX_ACTIVATEAREAS search (SOS). + + * code/client/cl_cgame.c: see below. + * code/cgame/cg_syscalls.c (trap_R_AddAdditiveLightToScene): below. + * code/cgame/cg_syscalls.asm: see below (trap_R_AddAdditiveLightToScene). + * code/cgame/cg_public.h: CG_R_ADDADDITIVELIGHTTOSCENE (SOS). + + * code/bspc/l_math.c: new VectorLengthSquared, removed rotate/matrix (SOS). + * code/bspc/bspc.c (BSPC_VERSION): was 2.1e, now? + * code/bspc/be_aas_bspc.c (BotImport_Trace): CM_BoxTrace sig. (SOS). + * code/bspc/aas_file.c (AAS_WriteAASFile): SOS. + * code/botlib/be_aas_sample.c (AAS_DeAllocAASLink): SOS. + + * code/unix/unix_main.c (Sys_LoadDll): do not load from installdir + in NDEBUG (confusing relic from old Makefile). Postfix -debug.so + for debug binaries to let both builds coexist. + + * code/unix/Makefile: updated install targets and VERSION. + + * Win32: build from SOS snapshot. + Note: Unix CR/LF in *.dsw/*.dsp fucks up MSVC++. + +2001-02-02 Bernd Kreimeier + + * SOS: all changes up to today. + + * code/server/sv_init.c (SV_TouchCGame): added. Also memset + on reallocated client data (SOS). + * code/qcommon/qcommon.h: see below. + * code/qcommon/cvar.c (Cvar_SetLatched): new (SOS). + * code/qcommon/cm_trace.c: more sphere test fixes (SOS). + Note: SOS encryption key expired and updated by MrElusive. + + * code/qcommon/cm_patch.c (CM_TraceThroughPatchCollide): + fix from MrElusive, fall through curved corner floors (q3dm17). + Later: also in SOS (so is shadow FPE fix). + + * Win32: can't get an unadulterated SOS snapshot to build. + First, fix CR/LF back again (Linux client converts all). + find . -name '*.ds*' -print + alias dos2unix='recode ibmpc..lat1' + alias unix2dos='recode lat1..ibmpc' + Next, find a *.dws that works? Nope, no cigar. + +2001-02-01 Bernd Kreimeier + + * Win32: have to update dsp/dsw/etc. files in CVS, too. + + * CVS: tag previous version before update + cvs tag id1-27h-loki010131-beta3 + + * code/game/q_math.c (Q_rsqrt): guard, #ifndef __linux__ + for assert (for Win32 build). + TODO: assert replacement for Win32? + * code/q3_ui/ui_qmenu.c: see below. + * code/q3_ui/ui_players.c: see below. + * code/q3_ui/ui_controls2.c: float const with f postfix + Note: Win32 C4305 warning. Somebody at id has been doing + a lot of these recently as well... + + * code/cgame/cg_players.c (CG_PlayerShadow): applied fix by + MrElusive, removed FPE hack (player shadows on zero mormals). + Prolly in this evenings' CVS. + + * code/server/sv_game.c: new signatures (capsule again). + * code/server/server.h: new signatures (SV_Trace,ClipToEntity). + * code/server/sv_bot.c: new signatures (above). + * code/qcommon/cm_trace.c: a truckload of changes. Math + code added before moved upwards. Capsule traces added all + over the place, old box traces moved in conditional + branches, functions renamed and wrapped. Eliminated some + of the previous' versions deadcode to keep diffs smaller. + TODO: once a point release is out and reasonably bug + TODO free, remove // bkYYMMDD annotations where SOS related. + + * code/qcommon/cm_public.h: new signatures in prototypes. + * code/qcommon/cm_patch.c: dead code re-enabled, new + sections (conditional branches for spheres) added to + several trace functions. + * code/qcommon/cm_local.h (CAPSULE_MODEL_HANDLE): added. + * code/qcommon/cm_load.c (CM_TempBoxModel): capsules. + + * code/game/q_shared.h (Q3_VERSION): 1.27i now (new QVM traps). + + * code/game/g_syscalls.asm: see below. + * code/game/g_public.h (SVF_CAPSULE): added (SOS). Also + G_ entry poiints for capsule traces. + + * code/client/cl_cgame.c: see below. + * code/cgame/cg_syscalls.c: see below. + * code/cgame/cg_syscalls.asm: see below. + * code/cgame/cg_public.h: new capsule trace code (SOS). + +2001-01-31 Bernd Kreimeier + + * Win32: test compile (WinCVS, MSVC++). Have to guard isnan. + Note: too much shit going on.... + +2001-01-30 Bernd Kreimeier + + * CVS: update for patching up (pre-1.27i). + + * SOS: new changes (new collision detection primitives). + Now Version 1.27i. + TODO: start testing using DLL's (QVM code is out of sync). + +2001-01-25 Bernd Kreimeier + + * SOS: caught up till today (below). + * code/qcommon/cm_trace.c: new functions added: RotatePoint, + TransposeMatrix, CreateRotationMatrix (SOS). + (CM_TransformedBoxTrace): new rotation code used here. + + * code/q3_ui/ui_demo2.c: sizeof(extension). SOS. + * code/game/g_cmds.c (G_SayTo): CON_CONNECTED. + * code/game/ai_main.c: HOOK added (SOS). + * code/botlib/be_aas_move.c (AAS_HorizontalVelocityForJump): + correct fix for FPE occuring (SOS). + * code/game/ai_dmq3.c: initmove.viewoffset (SOS). + + * code/game/q_math.c: guard asser/isnan with Q3_VM (q3asm). + TODO: define Com_Error based assert macro? NDEBUG? + +2001-01-24 Bernd Kreimeier + + * code/server/sv_ccmds.c (SV_MapRestart_f): some debug. + TODO: map_restart 0 disconnects external client in 1.27h? + + * code/renderer/tr_image.c (LoadTGA): added some commentary + and dead code based on fixes from GtkRadiant (Leonardo found + flipped TGA's). + +2001-01-23 Bernd Kreimeier + + * BETA3: finished testing, ready to upload to id FTP. + Later: neither the FreeBSD beta not the Linux Beta3 + uloaded. Beta2 not yet released, and clients get + disconnected with Beta2 and Beta3 on SV_MapRestart_f. + +2001-01-22 Bernd Kreimeier + + * code/client/cl_main.c (CL_InitDownloads): undid yesterday (SOS). + * code/botlib/be_aas_sample.c (AAS_DeAllocAASLink): guard print (SOS). + * code/server/sv_client.c (SV_DirectConnect): VM_Call disconnect (SOS). + * code/qcommon/files.c (FS_ListFilteredFiles): trailing slashes (SOS). + * code/game/g_cmds.c (SetTeam): print change (SOS). + Note: the above plus VectorClear(v1) (below) are todays SOS changes. + + * code/cgame/cg_players.c (CG_PlayerShadow): ignore bogus + (all zero) planes. This caused FPE in ProjectPointOnPlane. + TODO: why does trace return zero normal planes? + Note: gdb seems totally at loss with vec3_t arrays.... + + * code/botlib/be_aas_sample.c (AAS_TraceAreas): FPE. + NaN in uninitialized v1 that wasn't supposed to be referred + to in this branch. + + * code/botlib/be_aas_move.c (AAS_HorizontalVelocityForJump): + FPE divide by zero (zero zvel, zero t) for jump estimates. + + * code/client/cl_main.c (CL_Frame):1856. uivm==NULL on + client after server crashed. + TODO: check that uivm always non-NULL for client. + TODO: do setenv(FX_NO_SIGNALS) to avoid exit errors... + + * code/unix/linux_glimp.c (GLW_SetMode): added "Indirect" + Mesa token to software rendering detection. Reworded error + output and added drivername. + TODO: measure framerate instead? + +2001-01-21 Bernd Kreimeier + + * SOS: caught up with changes up until today. + + * code/server/sv_init.c (SV_SetConfigstring): gentity != NULL + + * code/server/sv_client.c: connect to "{all bots" server. + * code/renderer/tr_init.c: JPEG extension on screenshots + * code/qcommon/files.c: modes based on mods, fs_basegame + + * code/q3_ui/ui_demo2.c: dm3 extension (demo names, protocol). + + * code/game/g_client.c: savedEvents[] removed. + * code/game/bg_misc.c: event sequence fixes. + * code/client/snd_dma.c (S_StopBackgroundTrack): different use. + * code/client/cl_main.c: demo file handling changed (names). + Also CL_InitDownloads: always next download. + + * code/cgame/cg_servercmds.c: cg_thirdPerson. + * code/cgame/cg_weapons.c: see below. + Also CG_ShotgunPattern: different call (seed parameter). + + * code/cgame/cg_main.c: see below. + * code/cgame/cg_local.h: new cg_noProjectileTrail Cvar. + * code/cgame/cg_effects.c (CG_BubbleTrail): early out (above). + + * code/bspc/l_poly.c (BOGUS_RANGE): increased. + * code/bspc/bspc.c: applied patch up to "2.1e" + +2001-01-18 Bernd Kreimeier + + * code/ui/ui_main.c: below. + * code/q3_ui/ui_main.c: UI_HASUNIQUECDKEY comment. + Note: mods have to return qfalse. See Bug #2890 in Fenris. + +2001-01-17 Bernd Kreimeier + + * BETA2: finished testing, uploaded to id's FTP for release. + +2001-01-16 Bernd Kreimeier + + * CVS: checking in preparation for Beta2. + cvs tag id1-27h-loki010116-beta2 + + * SOS: new bspc "2.1e". No change on 1.27h. + + * TEST: patch-up seems to work fine. No new files have been added + to the linkage (i.e. the ft2/ files now added), so we might not be + feature complete. + + * code/game/g_active.c (ClientThink_real): id MISSIONPACK + conditional in addition to the ones I added earlier. + * code/qcommon/files.c: REJECT. Linux hack for userdir threw it off. + * code/qcommon/unzip.c: REJECT. CRC-32 section removed. + Later: unused tempB + + * code/q3_ui/ui_syscalls.asm: REJECT. Start/StopBackgroundTrack. + * code/ui/ui_syscalls.asm: REJECT. syscalls ids from 1.27h + as of SOS (floor/ceil - will this ever get straightened out) + * code/win32/win_input.c: REJECT. g_pMouse edit. + * ui/menus.txt: REJECT. Replaced with 1.27h version. + Note: some more due to $SOS$. + + * ui/: new scripts. + cinematicmenu.menu, demo_quit.menu, ingame.txt, serverinfo_old.menu + vid_restart.menu + + * code/ft2/ttconfig.h: below. + * code/ft2/sfconfig.h: below. + * code/ft2/pstables.h: below. + * code/ft2/psnames.c: below. + * code/ft2/psdriver.h/c: below. + * code/ft2/keys.h: below. + * code/ft2/ftbbox.c: new in 1.27h + + * code/cgame/cg_newdraw.c: beware: cg_newDraw.c gets lost in diff easily. + + * code/cgame/cg_rankings.c: file removed from SOS. + +2001-01-15 Bernd Kreimeier + + * Patch-up: patching up from RC4 to 1.27h current. + No changes since 010112 snapshot. + ln -s sos010112/ work + diff -urbB sos001204-rc4/ work > work.diff + ln -s cvs-1.27g/ work + patch -p0 < work.diff > work.patch + find cvs1.27g/ -name '*.rej' -print + + * SOS: adding the remaining SOS snapshots to CVS. + cvs import Quake3_sos sos001211 pr1-27g-win32-001211 + Note: at this point id warned about repository corruption. + Watch out for the syscall stuff in particular. + cvs import Quake3_sos sos010104 pr1-27g-win32-010104 + cvs import Quake3_sos sos010108 pr1-27h-win32-010108 + cvs import Quake3_sos sos010110 pr1-27h-win32-010110 + cvs import Quake3_sos sos010112 pr1-27h-win32-010112 + Note: the first 1.27h might be the public (server only) + beta released, the second one was post release. Beware + of source files added and removed (botlib headers, FT2). + Note: why so late? Don't ask... + +2001-01-08 Bernd Kreimeier + + * SOS: id's working up to 1.27h (server side fix for + Guard exploit seems to force earlier release). Updating + CVS (most of the changes are debug code put in and + then disabled, plus some fixes as below). Next patching + up to current SOS. + +2001-01-07 Bernd Kreimeier + + * Makefile: need to rework this for multiple platforms. + We also need null/null_vm.c for platforms where we don't + have JIT (assembly emit). + +2001-01-04 Bernd Kreimeier + + * code/q3_ui/ui_connect.c (UI_DisplayDownloadInfo): time + information for current (vs. start of download) is wrong, + thus negative 1 "estimated time", as well as transfer + rate just negative downloadSize. Not fixed. + + * code/unix/unix_main.c (Sys_ParseArgs): added. + Note: for support/us, to identify builts. This is only + a skeleton right now - if I ever feel the need to support + more than "-v" and "--version" I'll have to flesh this out. + + * code/unix/linux_glimp.c (signal_handler): see below. + * code/unix/unix_main.c (Sys_Exit): added an abstraction + layer for exit/_exit/assert/raise issues. + Note: need both a better debug/backtrace handling, and + have to find a way to determine why/where the alleged + startup/exit errors happen... + +2001-01-03 Bernd Kreimeier + + * code/game/g_mem.c (G_Alloc): ERR_DROP initiated by + addbot commands for large sv_maxclients, allegedly + caused segfaults in 1.17. Not reproducible. + TODO: recover more gracefully from failure to add bot? + + * code/renderer/tr_light.c (R_LightForPoint): Tim Angus + reports a crashbug with nolight maps. Also assertion in + R_SetupEntityLightingGrid, might want conditional there. + DONE: fixed crash on LightForPoint for nolight maps. + + * code/qcommon/qcommon.h: NUM_SERVER_PORTS. A feature + request to increase this, or make it more flexible + otherwise (Fenris). + TODO: id decision on more flexible NUM_SERVER_PORTS. + +2001-01-02 Bernd Kreimeier + + * code/unix/snapvector.nasm: fixed FPU bit (the current + one had reserved bits off, behavior should not change). + * code/qcommon/vm_x86.c: fixed symbols (below). + * code/unix/ftol.nasm: FPU bits weren't correct (duh). + DONE: shoot-though floor (q3dm5) + DONE: cursor-in-rect off (TA/Player model selection) + Note: in gdb, "disassemble " is your friend. + + * code/cgame/cg_public.h: CG_MEMSET is set to 100. In + cg_syscalls.asm it's 101. If I change it I get Bad trap 100 + from the cgame VM code, so the 1.27g "official" VM code + uses it. + + * code/unix/linux_common.c: have to fall back to C, the + current assembly is buggy... + * code/unix/Makefile (linux_common.o): added. + Later: also for dedicated. Less portable this way. + TODO: C_ONLY for dedicated on non-i386 only? + + * code/qcommon/common.c: do not use memcpy/memset under Linux. + * code/unix/linux_common.c: added Andrew's assembly port. + TODO: C_ONLY for Com_Memset/Memcpy? Conditionals are fubared. + + * code/qcommon/vm.c (VM_Init): use Win32 defaults (do not + use DLL's by default). This exposes DLL rounding errors + (damage through floors), and we don't want DLL's used by + default anyway. + TODO: why vm_ui default of 1? + + * code/botlib/l_precomp.c (SourceWarning): removed assert. + + * code/game/bg_lib.c (acos): defined, but we don't actually + use it except where the cg_syscalls.asm trap is used. + + * code/game/g_public.h: missing lots of trap tokens. + * code/game/g_syscalls.c: missing lots of traps. + * code/game/g_syscalls.asm: more inconsistent hooks, were: + equ floor -111 + equ ceil -112 + equ testPrintInt -113 + equ testPrintFloat -114 + now changed to match cg_syscalls. + Note: fixed this in UI earlier, how did this slip through + the diffs against SOS? + + * code/game/g_syscalls.c: no acos hook. + * code/cgame/cg_syscalls.c: no acos hook. + * code/cgame/cg_syscalls.asm: has acos hook as -112 + Note: report from Tim Angus. The acos function is in bg_lib.c + which is linked only into ui (not q3_ui). That means we are + using libc acos right now? + Note: QVM traps are negative? + + * BSD/Irix: tagged current CVS (not all of the below) as + cvs tag id1-27g-loki010102-bsd1 + for BSD work (Rafael Barrero). Also be used for Irix update. + +2001-01-01 Bernd Kreimeier + + * SOS: adding the remaining SOS snapshots to CVS. + cvs import Quake3_sos sos001201-rc3 pr1-27f-win32-001201-rc3 + cvs import Quake3_sos sos001202 pr1-27f-win32-001202 + cvs import Quake3_sos sos001204 pr1-27g-win32-001204-rc4 + This is the codebase to which the Linux branch has been patched + up. I can't verify whether this is identical to the RC4 codebase + as the tag doesn't work (but can check against the ZIP file..) + cvs import Quake3_sos sos001211 pr1-27g-win32-001211 + The above snapshot contains a (post-release?) fix to ui_syscalls + in ui/ and q3_ui/. This change has been used in Linux (Beta1 and + above). At this point, id discouraged further use of SOS due to + repository corruption on their end. No further snapshots were + taken since. + + * Fenris: since the release of the Beta1 bugs have been + maintained at http://fenris.lokigames.com/. I am going to + list issues here as they get fixed. + +2000-12-21 Bernd Kreimeier + + * code/renderer/tr_font.c: graceful silence with old mods? + * code/botlib/l_precomp.c (SourceWarning): graceful exit if old mod? + +2000-12-20 Bernd Kreimeier + + * code/server/sv_ccmds.c (SV_MapRestart_f): see below. + * code/qcommon/vm.c: currentVM is 0x0 in VM_ArgPtr. + In VM_Call, oldVM was NULL - made conditional the + reset of currentVM to oldVM. + +2000-12-18 Bernd Kreimeier + + * BETA1: closed Linux beta release. Stripped debug + and release binaries, DLL's, and pak4.pk3. CVS checkin, + will be tagged as + cvs tag id1-27g-loki001218-beta1 + Later: id added a pak5.pk3 to the Win32 point release, + added this to the BETA1 best. + + * code/qcommon/vm_x86.c: C37F. + * code/unix/snapvector.nasm: C37F. + Note: short of any real evidence, I gamble and use max. + precision (as well as default Linux precision, but NOT + Win32 precision). It seems that precision change is not + really an issue (despite Graeme's claim that the cursor + in the menu was/is off). I also pick the roundiung behavior + that is seemingly used by ANSI and gcc (but possibly not + Win32 _ftol depending on build). + +2000-12-15 Bernd Kreimeier + + * code/unix/Makefile: added snapvector.o + * code/unix/unix_shared.c: #if 0'ed the old snapvector code. + * code/unix/snapvector.nasm (Sys_SnapVectorCW): two new + assembly functions from AndrewH that explicitely set the + FPU control word to convert vec3_t, to ensure cross-platform + behavior for both DLL and QVM. + + * code/unix/ftol.nasm (Q_ftolC37F): for globals. + + * code/unix/unix_main.c: took out global FPU manipulation. + For clarity this should be VM only. + * code/qcommon/vm_x86.c: added prototypes for the ftol + library. To select a specific behavior for the entire VM, + set ftolPtr accordingly. + Later: the GCC ftol function of course affect the stack + (there is no "declspec naked"). The problem seems to be + that the VM never handles the stack in a way compatible + to regular gcc C functions. For some odd reason _ftol seems + to do the right thing under Win32. All 4 control words + implemented at the moment work just fine with the menus. + + * code/unix/ftol.nasm: added a small library of "safe" qftol + variations that explicitely set the control word to the + relevant (4) possibilities. + +2000-12-13 Bernd Kreimeier + + * code/qcommon/vm_x86.c: an entire day spent trying to nail + the ftol issues. It breaks down like this: id used to use + an unsafe (no setting FPU control word) fistp. That seemingly + caused subtle physics bugs which nobody cared about in 1.17. + They then changed the UI code, and ran into the UI bugs: + menu entries shifted to the right, fonts vanishing. Then + they switched to using _ftol. Then they had to reproduce + the old behavior for the physics code due to public outrage. + My original port used a simple (long)float cast, which gcc + seemingly compiles to code that does OR 0C00 on whatever + current control word (precision unchanged). This breaks the + menus. If I use the unprotected fistp instead, which should + (Linux 037F default) use "nearest/even", then my menus are + correct. That would mean Win32 _ftol in id's compile does + the same, only that would require /qifist or some equivalent + compile flag, which I can't find. Two disassemblies of _ftol + I got from others showed OR 0C00 as part of the default (ANSI) + behavior. + +2000-12-13 Bernd Kreimeier + + * code/game/bg_pmove.c (PmoveSingle): trap_SnapVector. + The one true and single call to snap velocity. + Note: bspc/map.c:void SnapVector(vec3_t normal) + qcommon/cm_patch.c:void CM_SnapVector(vec3_t normal) + game/q_shared.h: #define SnapVector(v) {v[0]=((int)(v[0]));... + + * code/client/cl_cgame.c: CG_SNAPVECTOR. + * code/server/sv_game.c: G_SNAPVECTOR. + Note: these go through trap_SnapVector in syscalls. + + * code/unix/unix_shared.c (Sys_SnapVector): sticking to + old Linux version for now... + * code/win32/win_shared.c (Sys_SnapVector): changed. + Note: Graeme points out this was changed to fix ftol + artifacts? + TODO: calculate errors for various ftol variants... + + * code/qcommon/vm_x86.c: both the old fistp code (1.17) + and the new qftol function apparatently work. Using the + ftol.nasm code for now. + + * code/unix/Makefile: DO_NASM and ftol.o. + + * code/unix/ftol.nasm (qftol): created from Mike's SoF + replacements, with Andrew's help to satify the VM + stack/call requirements. + TODO: use Q_ftol herein to replace myftol elsewhere. + + * code/unix/unix_main.c (Sys_ConfigureFPU): SIGFPE. + TODO: divide by zero in botlib. Disable this for now. + Note: we can't introduce calculation differences between + versions, so fixing these will have to wait. + + * code/qcommon/vm_x86.c: two new lines in Win32 branch + missing from Linux assembly in AsmCall: + mov eax, dword ptr [edi] + and eax, [callMask] + Added, doesn't seem to affect UI etc. bugs. + Later: no FTOL_PTR, use fistp non-IEEE assembly as in old + version. This seems to work for Q3 and TA, while qftol + (simple cast) does not - for Win32 Graeme says the reverse + is true. + + * code/qcommon/vm_x86_old.c: used the old cvs-1.17 version. + Two fixes (Hunk_Alloc, Com_Memcpy), and it works: + +set vm_game 2 +set vm_ui 2 +set vm_cgame 2 + UI, cgame and game w/o apparent problems. + +2000-12-12 Bernd Kreimeier + + * code/unix/Makefile: cleanup of redundant flags. + Removed bogus MALLOC_CHECK (note to self: export MALLOC_CHECK_=2). + Also DO_SHLIB_CC on all UI DLL's. + Added and removed DEBUG_VM flag. + TODO: figure out whether Zoid did UI this way intentionally. + Note: this seemingly fixed the botimport problem, although + most of the changes were just redundant CFLAGS removed. Given + our wanker toolchain, should have been more paranoid. All + DLL's can now be used w/o apparent problems. + + * code/server/sv_main.c: gvm init. + * code/server/sv_game.c: gvm assertions. + * code/unix/unix_main.c (Sys_LoadDll): print vmMain + Note: top no avail. There is some odd ld/gdb problem here + that prevents examining globals and obfuscates part of + the stack between VM_Call and lower level code, through + G_InitGame. This is not just DLL's being loaded and unloaded. + Wromg flags during build? The vmCvar for "bot_developer" + ends up overlapping global botimport in memory, which + thus zero-fills part of the function pointer table. + + * code/server/sv_bot.c (SV_BotInitBotLib): this (by way of + GetBotLibAPI) is responsible for setting botimport, which, + if using the game DLL, is not properly set up. Called in + SV_Init(). + + * code/game/q_shared.c: Q_strncpyz does zero padding (duh). + Note: calls strncpy, which does a zero fill up to destsize. + If destsize exceeds memory size, zero padding will overwrite + adjacent memory. Suspicion was this happend to botimport. + + * code/qcommon/cvar.c: possible problem in Q_strncpyz call. + + * code/botlib/be_ai_weap.c (weaponinfo_fields): made this static. + Note: it seems that the "number" string got replaced by + p def.fields[0] + {name = 0x40000000 "\177ELF\001\001\001", offset = 2, type = 50, .. + Memory corruption? + + * code/game/inv.h (WEAPONINDEX_GAUNTLET): defined here. + * botfiles/weapons.c (Gauntlet): the baseq3/qagamei386.so parser + breaks here: + number WEAPONINDEX_GAUNTLET + * code/botlib/l_precomp.c (SourceWarning): added assertion to + trap botlib parsing problem.. + + * RC1: for beta test. Using my own vm/ui.qvm files in this case. + TODO: Setup with nouninstall. + TODO: fix game DLL/ botlib setup problem (so all DLL's work) + TODO: SIGFPE + TODO: profile? + + * code/unix/Makefile (ai_vcmd.o): added to game DLL linkage. + How the fuck did this happen? + DONE: "qagamei386.so: undefined symbol: BotVoiceChat_Defend" + + * TEST: +set vm_ui 2 (vm_x86, not interpreter). Breaks! + Further: qagame had undefined, but seemingly gets reloaded + second try (I hate the Linux linker). + * TODO: never reload fail DLL, abort engine + + +2000-12-11 Bernd Kreimeier + + * TEST: recompile QVM/DLL and executable to test new UI code. + The UI QVMs from the paks still do not work. + + * SOS: changes in UI code! + * code/q3_ui/ui_public.h: this file is deprecated + Note: e.g. it does not contain the background track calls. + * code/ui/ui_public.h: the uiImport_t enum here determines + the values. + * code/ui/ui_syscalls.asm: same as q3_ui now, were: + equ floor -111 + equ ceil -112 + * code/q3_ui/ui_syscalls.asm: these are now switched, were: + equ trap_S_StartBackgroundTrack -63 + equ trap_S_StopBackgroundTrack -64 + The new values match the ui/ equivalent. Also, floor (-108) + and ceil (-109) are different in ui/. + + * CVS: going to check in this snapshot and tag it as + cvs tag id1-27g-loki001209-rc4 + Presumed equivalent to SOS tag "1.27g RC4" (master). As I can't + obtain the tagged code using SOS (neither Win32 nor Linux client) + I can only guess. + + * TEST: use my own VM code, ion baseq3/vm/*.qvm and missionpack/vm/. + This works - in other words, the menu bug seems in the UI code, and + is fixed in my codebase. + + * TEST: make release. + Note: I can postpone DLL specific problems. Bad performance is not + as important as outright bugs. Thus the UI QVM issue is the only + one that stops me from creating an RC. + TODO: Q3 UI QVM code from pak file does not work (neither does TA). + TODO: sound with video playback still awful. Threaded sound, I guess. + TODO: ERROR: couldn't open demos/DEMO002.dm3.dm_48 (same demo001.dm3.dm_48) + + TODO: do not show Q3 demos in TA menu? + TODO: new demos for Q3? Or at least error message? + + * code/game/bg_lib.c: itrinsics excluded by Q3_VM (another -O + compile). Uninitialized variable. + * code/unix/Makefile: -O for uninit on patched code. Also shortcuts. + TODO: DC_ONLY seems an obsolete flag, used in Makefiles, not source. + + * TEST: +set sv_pure 0 +set vm_game 1 +set vm_cgame 1 +set vm_ui 0 + Turns out that the pak0.pk3 UI QVM code is seemingly broken in TA + and Q3, but my UI DLL is not. In reverse, the QVM game/cgame for + Q3 seems to work quite well (including bots). The TA game/cgame + also works, including botlib init. + TODO: BotLib Init using game DLL gives: + TODO: Error: file weapons.c, line 38: unknown structure field number + TODO: Fatal: couldn't load the weapon config + TODO: Error: BotLoadMap: bot library used before being setup + + * TEST: checked the rc4winstlr.zip CD tree against + my test install. baseq3/pak4.pl3 and missionpack/pak0.pk3 + are identical, but I finally recognized that there was + a missionpack/pak1.pk3 not in the final install - left over + from an earlier update from id. Doesn't seem to affect the + DLL based runs at all. + Note: I still do not have the final CD snapshot Robert + promised me mid last week, they haven't even fixed the + FTP account they took down. Communication with id is as + abyssmal as ever. + +2000-12-08 Bernd Kreimeier + + * TEST: running with RC4 data files. + TODO: "bot library used before setup" (Q3+TA) + TODO: Q3 old mods wreak havoc (graceful bounce) + TODO: supress "FreeType code not available" in renderer + TODO: can't move in Q3 + TODO: items flicker in Q3 + TODO: no decals in Q3 + TODO: VM UI code still broken (Q3+TA) + TODO: sound code is awful + TODO: video playback inferior to earlier builds + + * code/q3_ui/ui_local.h: prototype trap_VerifyCDKey(..) + * code/game/g_active.c ( StuckInOtherClient): TA only. + * code/cgame/cg_draw.c: 4x unbalanced `#endif' - from patch? + * code/null/null_client.c (CL_CDKeyValidate): dummy added. + * code/qcommon/common.c: Q_acos missing, changed conditionals + + * code/qcommon/vm_x86.c: unreacheable _asm instruction that + gcc doesn't quite like... #if 0'ed for now + TODO: understand _asm { mov eax,[ebx] }, fix it for gcc + + * TEST: compile... + + * code/ui/ui_main.c: full REJECT. Manual merge. + Note: preserved debug_protocol lines, who knows what it's good for. + + * code/qcommon/files.c: REJECT. SafeMode, demo server FS_Restart. + + * code/client/snd_mem.c: REJECT: $SOS$. + * code/client/snd_dma.c: REJECT: $SOS$. + * code/client/cl_cin.c: REJECT. com_timescale, $SOS$. + + * code/cgame/cg_draw.c: REJECT. Lots, but virtually all either + float postfix (on some, not all places), or #ifndef MISSIONPACK + that I had already put in during -Werror (conditional unused). + + * code/cgame/cg_consolecmds.c: REJECT. id commented unused code + that I had #if 0'ed earlier. + + * code/game/: three REJECT for $SOS$. + * code/botlib/: lots REJECT for $SOS$. + + * Patch: patching up from demo source. + ln -s sos001204-rc4 work + diff -urbB sos001122-demo/ work > work.diff + ln -s cvs-1.27b/ work + patch -p0 < work.diff > work.patch + find cvs1.27b/ -name '*.rej' -print + + * CVS: going to check in this snapshot and tag it as + cvs tag id1-27b-loki001208-demo + Then patching up to RC4, as of sos001204-rc4 (no changes since, + should be equivalent to SOS tag "1.27g RC4" (raduffy), i.e. master. + + * TEST: installed demota/ from Win32 distribution. Binary + fails claiming "Corrupted pak0.pk3". Abandoned. + Note: a Linux demo for Q3TA has no priority. Most important is + the Q3A point release in time for Q3TA hitting shelves, followed + by testing for Q3TA. The source is in CVS and tagged (see above) + in case a demo matching the released files has to be provided + later. + + +2000-12-07 Bernd Kreimeier + + * TEST: compile and link - succeeds. + + * code/ui/ui_main.c: UI_StopServerRefresh now uaws. + New unused variables. + + * code/unix/unix_main.c: added Sys_LowPhysicalMemory() stub. + TODO: write Linux equivalent to GlobalMemoryStatus. + + * code/qcommon/common.c: Com_Memset/Com_Memcpy. Neither assembly + nor C versions included if not on Win32 i386. + TODO: using/porting assembly? + + * code/qcommon/files.c: unused variable. + TODO: fs_scrambledProductId unused if 0 for now. + Note: -DFS_MISSING for id's pak cleanup, not used. + + * TEST: compile and link - fails. + + * code/macosx/Client/Makefile.postamble: empty ORIG. + * code/macosx/Client/Makefile.preamble: ORIG. $(BOTLIB_OBJS) added. + + * code/server/sv_client.c: ORIG. Com_Memset. + * code/renderer/tr_shader.c: ORIG. Com_Memset, CIN_Shader. + * code/qcommon/vm_x86.c: ORIG. Com_Memcpy. + * code/qcommon/unzip.c: REJECT. Com_Memcpy, $SOS$. + * code/qcommon/qcommon.h: ORIG. PROTOCOL 47, plus Sys_LowPhysicalMemory. + * code/qcommon/md4.c: Com_Memset,Com_Memcpy (ORIG). + * code/qcommon/files.c (Sys_ConcatenateFileList): REJECT. + Our additons threw it off, plus $SOS$. + * code/qcommon/common.c: they fixed same unused variables (REJECT). + + * code/ui/ui_shared.c: additions (ORIG). + * code/ui/ui_gameinfo.c: COM_Compress added (ORIG). + * code/ui/ui_atoms.c: print statements removed (ORIG). + * code/ui/ui_main.c (UI_DoServerRefresh): REJECT on comment edit... + + * code/game/g_cmds.c (Cmd_VoiceTaunt_f): logic changed heavily. ORIG. + * code/game/q_shared.h: Q3_VERSION "Q3 Team Arena Demo 1.27b" + plus Com_Memset, Com_Memcpy, CIN_shader, COM_Compress. + * code/game/g_main.c: Cvar change only + * code/game/ai_dmq3.c: $SOS$. + + * code/client/snd_mix.c: Com_Memset + * code/client/client.h: additions (ORIG). + * code/client/snd_mem.c: see below. + * code/client/snd_dma.c: $SOS$ (CVS keyword). + + * code/client/cl_cin.c: they removed unused (REJECT). + * code/cgame/cg_servercmds.c: ORIG. compress, noTaunt etc. + * code/cgame/cg_main.c: ORIG. Conditonal branch, COM_Compress. + * code/cgame/cg_consolecmds.c: ORIG. Cvar values changed. + * code/cgame/cg_draw.c (CG_DrawTeamBackground): ORIG. + no reject but *.orig file created. I just mark spots were + code changed after verifying the patch succeeded. + + * code/cgame/cg_event.c: fixed reject (REJECT). + * code/botlib/: all *.rej here due to SOS/CVS $Keyword$. + TODO: preserve SOS comments/rev history somehow. + + * Patch: patching up to demo source. + ln -s sos001122-demo work + diff -urbB sos001119/ work > work.diff + ln -s cvs-1.26/ work + patch -p0 < work.diff > work.patch + find cvs1.26/ -name '*.rej' -print + + * CVS: going to check in this snapshot and tag it as + cvs tag id1-26w-loki001207 + to prepare for upgrading to RC4. I have already made + many more changes than I wanted to w/o getting any + closer to pinpointing the problem, I might as well + patch up to id's more current sources. + + * code/botlib/be_interface.c: initialize by memset. Turns + out that this fails in Export_BotLibSetup on BotSetupWeaponAI + loading "weapons.c" (from the pak, presumably) with an unknown + structure field number. Mismatch of datafiles vs. source again. + + TODO: id replaced memsets in later source. + TODO: have memsets on all exports and imports. + + * SOS: RC4 source should be tagged "1.27g RC4" (raduffy). + Unfortunately the Linux client doesn't care a bit. Show + History does work if from/to date differ by at least a + day, and it shows the tag on code/ (only that subtree), + but recursive get aborts halfway. + Manual: http://www.sourcegear.com/SOS/Doc/ + +2000-12-06 Bernd Kreimeier + + * TEST: accepting missing shaders now. No bots, but I can + actually enter the game and play (more than can be said for + classic Q3 right now). + TODO: Error: BotStartFrame: bot library used before being setup + + * code/renderer/tr_shader.c: took out assertion for now... + * TEST: now missiopack/cgame loads + TODO: tr_shader.c:2275: R_FindShaderByName: failed + TODO: searches ui/assets/3_cursor2.TGA, has ui/assets/3_cursor3.tga + + * code/unix/Makefile (MPCGOBJ): ui_shared.o (duh). + DONE: /cgamei386.so: undefined symbol: PC_Float_Parse + + * code/botlib/be_ai_goal.c: initialize campspots etc. This + might or might not fix this one (didn't get back to gdb due + to mouse-only navigation). + DONE: 0x80d1d5b in BotFreeInfoEntities () at be_ai_goal.c:447 + + * TEST: this time with missionpack/cgame loading... noy + TODO: TA menu blocked after end of intro movie + TODO: console in_mouse 1 doesn't grap pointer even on vid_start? + + * code/cgame/cg_newdraw.c: -Werror. + * code/unix/Makefile (MPCGOBJ): cg_newdraw.o was missing (duh). + DONE: missionpack/cgamei386.so: undefined symbol: CG_OwnerDrawVisible" + + * code/ui/ui_shared.c:1309 assign after bail on NULL. + DONE: segfault in Item_SetFocus (item=0x0, x=0, y=0) + + * TEST: new set of DLL's (this time hopefully correct). + All baseq3/ DLL's load, as does the missionpack/ UI DLL. + The menus now work in both (TA seems mouse-only on everything + but "Quit"). Segfault on delayed TA "Quit" (stack fubared): + #5 0x809fc28 in VM_Call (vm=0x88408a0, callnum=3) at ..//qcommon/vm.c:617 + #6 0x805aafc in CL_KeyEvent (key=9, down=qtrue, time=128644) cl_keys.c:1194 + TODO: TA menu's w/o mouse? + TODO: Win32 goes submenus but does not unfold + TODO: Linux does not go submenus + + * code/ui/ui_main.c: see below. + TODO: LCC gets fits - operands of = have illegal types + TODO: 'pointer to const unsigned char' and 'pointer to const char' + * code/ui/ui_shared.c: see below. + * code/ui/ui_gameinfo.c: see below. + * code/ui/ui_atoms.c: see below. + * code/game/g_bot.c: more cruft. + * code/cgame/cg_draw.c: loads of functions modified for + MISSIONPACK that aren't used at all for MISSIONPACK anymore. + Development relics. + + * code/cgame/cg_consolecmds.c: -Werror. + Note: due to Makefile error never ever compiled... + + * code/unix/Makefile: fixed various dependency errors + for game and ui library. + TODO: create a new Makefile with patsubst and rules. + TODO: why C_ONLY in the i386 dedicated server? + + * code/unix/unix_main.c: use dlerror() excessively. + Littered more unused DLL related functions with assert(0). + TODO: clean up Sys_Load/UnloadDll (a real mess) + TODO: remove Zoid code cruft (unused per-DLL functions) + + * code/game/bg_misc.c: changed G_Printf for Com_Printf. + This was undefined in baseq3/uii386.so preventing loading. + + * TEST: +set sv_pure 0 +set vm_game 0 +set vm_cgame 0 +set vm_ui 0 + Note: so far I used only the game DLL.. duh. + UI DLL fails to load: missing G_Printf. + + * code/unix/Makefile: -DMALLOC_CHECK in addition to + the -DZONE_DEBUG I have used since switching to calloc. + Using MALLOC_CHECK=1 for now, might use 2 if something + comes up. + + * code/renderer/tr_init.c (GL_SetDefaultState): it does get + called, but does not show up in the log. + + * TEST: tried executing a script - get bounced. + TODO: is there any way to jump into a map? + TODO: cl_cinematics 0 (supress all fullscreen RoQ) + Next: used r_logfile 200 in Win32 (RC4) and Linux. + There is a buckload of setup code seemingly not done + at all in Linux? Either that, or logging is enabled + with a delay in Linux. + + * code/unix/linux_glimp.c: fixed autorepeat (H2/Fakk2 way). + +2000-12-05 Bernd Kreimeier + + * code/renderer/tr_mesh.c: added assert there. + * TEST: menus and in-game drawing are just as they were with + the initial SOS001119 port. In addition: + R_AddMD3Surfaces: no such frame 0 to -2147483477 + for 'models/players/xaero/upper.md3' + R_AddMD3Surfaces: no such frame -2147483477 to 171 + R_AddMD3Surfaces: no such frame 171 to -2147483498 + ad nauseam (used as my player model). + Triggered: haveing a trRefEntity_t *) 0x41dbbd00 with + frame = -2147483477. Might be a red herring (PRINT_DEVELOPER), + ignore for now. + + * code/ui/ui_main.c: missing return. + * code/ui/ui_shared.c: excess byte in initializer (which gcc + did not caught, but LCC did). Also LCC complains about + missing returns, but gcc doesn't (neither says unreacheable + code though). If necessary (MsVC?) guard with Q3_VM. + + * code/q3_ui/ui_ingame.c: see below. + * code/q3_ui/ui_atoms.c: voidfunc_f. LCC warns about conversion + from `pointer to void' to `pointer to void function(void)' + being compiler dependent. Casting NULL. Guess what, doesn't fix + it either. + TODO: do not use these cursed scripts to generate VM code, + we do not have proper rules for LCC/q3asm, thus the files never + get updated. + + * code/unix/Makefile: for paranoia's sake recreated the 1.17 + compile for the UI DLL (where only q_shared/math were actually + compiled as DO_SHLIB_CC. + Later: switched to different gcc. + + * STATIC: remaining problems are vmMain (same entry point for all + DLL's), could use cgMain, uiMain and gMain here for HARD_LINKED. + Note: I don't think id has used this in ages. + Plus all the collisions in *_syscalls.c, which simply can't be + fixed cheaply. None is the superset of 2 others, neither seems + w/o overlap to others. Full stop. + + * code/botlib/be_aas_move.c: see below. + * code/game/ai_dmq3.c: VEC_UP/DOWN, MOVEDIR_UP/DOWN now static. + See also game/g_utils.c for existing static duplicates. + + * code/game/q_shared.h: #define stricmp strcasecmp + * code/unix/Makefile: no mo' -Dstricmp=strcasecmp, see q_shared.h + Also: no mo' -I/usr/include/glide, no FX + TODO: are we building against system GL headers? ../Mesa/? + + * code/q3_ui/ui_atoms.c: comment on duplication + * code/cgame/cg_drawtools.c: use UI/CGAME_HARD_LINKED on UI duplicates + TODO: does this UI_ code in cg_drawtools/ui_atoms belong into ui_shared? + + * code/unix/Makefile: use -DQ3_STATIC + * code/game/q_shared.h (*_HARD_LINKED): trigger on Q3_STATIC + Later: collision between UI and CGAME is still there. This fixed + the Com_Error, Com_Printf issues though + + * code/unix/Makefile ($(B)/q3static/ai_vcmd.o): this file was + missing, hence undefined symbol. + ($(B)/baseq3/game/ai_vcmd.o): same here. + ($(B)/missionpack/game/ai_vcmd.o): same here. + + * STATIC: cg_syscalls.c, g_syscalls.c and ui_syscalls.c alias. + Multiply defined symbols: + Com_Error, Com_Printf + VEC_UP, VEC_DOWN + MOVEDIR_UP, MOVEDIR_DOWN + vmMain + dllEntry + PASSFLOAT + trap_Error + trap_Milliseconds + trap_Argc + trap_Argv + trap_FS_FOpenFile + trap_FS_Read + trap_FS_Write + trap_FS_FCloseFile + trap_FS_GetFileList + trap_R_RegisterModel + trap_R_RegisterSkin + trap_R_RegisterFont + trap_R_RegisterShaderNoMip + trap_R_ClearScene + trap_R_AddRefEntityToScene + trap_R_AddPolyToScene + trap_R_AddLightToScene + trap_R_RenderScene + trap_R_SetColor + trap_R_DrawStretchPic + trap_R_ModelBounds + trap_UpdateScree + trap_S_StartLocalSound + trap_S_RegisterSound + trap_Key_IsDown + trap_Key_GetCatcher + trap_Key_SetCatcher + trap_GetGlconfig + trap_PC_AddGlobalDefine + trap_PC_LoadSource + trap_PC_FreeSource + trap_PC_FreeSource + trap_PC_ReadToken + trap_PC_SourceFileAndLine + trap_S_StopBackgroundTrack + trap_S_StartBackgroundTrack + trap_RealTime + trap_CIN_PlayCinematic + trap_CIN_StopCinematic + trap_CIN_RunCinematic + trap_CIN_DrawCinematic + trap_CIN_SetExtents + trap_MemoryRemaining + trap_SendConsoleCommand + trap_Cvar_Register + trap_Cvar_Update + trap_Cvar_Set + trap_Cvar_VariableValue + trap_Cvar_VariableStringBuffer + trap_RealTime + trap_SnapVector // used in game/bg_*.c, needs conditional + More aliasing between ui_atoms.c and cg_drawtools.c: + UI_DrawBannerString + UI_ProportionalStringWidth + UI_ProportionalSizeScale + Undefined symbol: ai_team.o: In function `FindHumanTeamLeader': + ai_team.c:1899: undefined reference to `BotVoiceChat_Defend' + Note: + + * code/game/g_main.c: unused. + * code/game/g_arenas.c: unused. + * code/game/ai_team.c: init. + * code/game/ai_dmnet.c: /* in comment (odd). + Note: why do these come up now but not earlier? + TODO: the make dependencies might target wrong files. + + * code/unix/Makefile (TARGETS): added q3static. + Note: this is baseq3/ + + * TEST: +set r_logfile 100. It seems that the addition of + code (add an assertion etc.) changes the behavio of the binary. + The intro cinematics code seems to suffer first - didn't play, + then played, then (another assert added) doesn't play. Watch + out for (missionpack): + UI_CIN_PlayCinematic + SCR_PlayCinematic( mpintro.roq ) + trFMV::play(), playing mpintro.roq + Also fails to exit cleanly: break gives + #0 0x401919ee in __select () + #1 0x400bbcb8 in __DTOR_END__ () + #2 0x4004baa1 in _XSend () + #3 0x452b009f in GLXRenderFlush () + #4 0x804ce0c in _XRead () + #5 0x40680813 in ?? () + Stack is corrupted. + Note: ~/.q3a/gl.log + TODO: write per-frame files (see Heretic2) + TODO: add Heretic2 QGL (more detail) + + * code/unix/linux_qgl.c (QGL_EnableLogging): fixed countdown + (i.e. propagated changes from win32/, see Fakk2). + + * code/unix/linux_glimp.c: fixed QGL_EnableLogging argument + to avoid cast error (always qfalse). + + * code/unix/Makefile (DEBUG_CFLAGS): use ZONE_DEBUG. + + * code/qcommon/common.c: replaced malloc with calloc calls. + + * code/q3_ui/ui_local.h: have to use ui/ui_public.h + * code/cgame/cg_servercmds.c: requires ../ui/menudef.h + + * code/cgame/cg_consolecmds.c: ui/ui_shared.h is unique. + * code/q3_ui/ui_public.h: make sure this won't be compiled. + * code/client/client.h: we have to include ui/ui_public.h. + Note: id is obviously maintaing only the ui/ headers, so the + headers in q3_ui/ are deprecated. + + * code/renderer/tr_shader.c: added assertions (see Ryan's Fakk2 + problems with missing shaders). + + * code/game/g_cmds.c: below. + * code/game/ai_vcmd.c: below. + * code/game/ai_team.c: below. + * code/game/ai_dmnet.c: below. + * code/game/ai_dmq3.c: below. + * code/game/ai_chat.c: below. + * code/game/ai_cmd.c: ../../ui/menudef.h (new Q3TA script directory). + + * code/cgame/cg_newdraw.c: make sure it won't compile w/o MISSIONPACK. + + * code/cgame/cg_servercmds.c: below. + * code/cgame/cg_event.c: below. + * code/cgame/cg_consolecmds.c: below. + * code/client/keys.h: below. + * code/client/client.h: below. + * code/q3_ui/ui_local.h: include from ../q3_ui/ not ../ui/. + Note: id seems to intentionally use the header from the new ui/. + + * Makefile: checked -I$(UIDIR), there is no such. That means all + files include directly, which means all (including Q3) are using + the new ui/ headers. + +2000-12-04 Bernd Kreimeier + + * RC4: released as 362101115 Dec 4 11:40 TA_Q3A_RC4.zip + + * TEST: the corrupted menu problem is back :-(. Looks like I am in + for a static link next. + + * code/unix/Makefile (clean2): fixed (not all new OBJ covered). + * code/q3_ui/ui_teamorders.c: -Werror. + * code/q3_ui/ui_team.c: -Werror. + * code/q3_ui/ui_qmenu.c (Bitmap_Draw): -Werror. + * code/q3_ui/ui_mods.c (UI_Mods_LoadModsFromFile): unused. -Werror. + * code/q3_ui/ui_controls2.c: -Werror. + * code/q3_ui/ui_atoms.c: -Werror + * code/null/null_client.c: -Werror. + * code/unix/linux_joystick.c: -Werror. + * code/unix/linux_glimp.c: -Werror. + * code/unix/linux_qgl.c: -Werror. + * code/unix/unix_shared.c: -Werror. + * code/unix/unix_net.c: -Werror. + * code/unix/linux_local.h: added missing prototypes. + * code/unix/unix_main.c: -Werror. Includes linux_local.h + * code/jpeg-6/jdmainct.c: see below. + * code/jpeg-6/jcmainct.c: variables called "main" (*moan*) + * code/jpeg-6/jcdctmgr.c (forward_DCT): -Werror. + * code/botlib/l_script.c (PS_ReadLiteral): -Werror + * code/botlib/l_precomp.c (PC_AddBuiltinDefines): -Werror. + * code/botlib/be_interface.c: -Werror. + * code/botlib/be_aas_reach.c: -Werror + * code/botlib/be_aas_cluster.c: -Werror + * code/game/be_aas.h: -Werror. + Note: MrElusive accumulates a lot of code history in nested comments, + which gcc doesn't like at all. #if 0'ed to avoid. + * code/qcommon/vm_interpreted.c: -Werror. + * code/qcommon/unzip.c: -Werror. + * code/cgame/cg_servercmds.c: -Werror. + * code/cgame/cg_main.c: -Werror. + * code/cgame/cg_drawtools.c: -Werror. + * code/game/bg_misc.c: -Werror. + * code/game/be_ai_move.h (bot_avoidspot_s): added. + * code/botlib/be_ai_move.c: removed typedef struct bot_avoidspot_s + * code/client/snd_mix.c: -Werror. + * code/qcommon/md4.c: -Werror. + * code/qcommon/common.c: -Werror. + * code/client/cl_keys.c: -Werror. + * code/client/cl_cin.c: -Werror, init local variables. + * code/unix/Makefile: -Werror. need -O for -Wall for uninitialized + Note: the above is the list of files that got touched during a pass + with -g -O -Werror -Wall flags (in the hope of finding uninitialized + memory and ambiguous statements). Most of the above are simply + unused variables (or even code). + + TEST: RC3 data files, but DLL's. + TODO: TA gets stuck in initial sound, doesn't play cinematics (sometimes) + TODO: Q3 intro movie looses sound after Sarge gets teleported + TODO: Q3 ingame renders world, weapon, muzzleflash, hud, can shoot, + TODO: but no movement, hud background is fubared. + + * code/cgame/cg_main.c: cg_singlePlayerActive + + * code/q3_ui/ui_login.c: doesn't seem to be used? + * code/game/g_rankings.c (G_RankRunFrame): doesn't seem to be used. + * code/q3_ui/ui.sh: disabled this. + * code/q3_ui/q3_ui.sh: changed include path to ../q3_ui/ (duh). + + * code/game/game.sh: changed include path to ../q3_ui/ which + is not in the Win32 batch file. + * code/cgame/cg_rankings.c: this does not seem to be included. + * code/cgame/cgame_ta.sh: added -DCGAME. Also added cg_syscalls.c + to build (also missing in Win32). + + * code/cgame/cgame.sh: added -DCGAME (see cgame.bat). Also + changed include path to ../q3_ui/ which is not in the Win32 + batch file. Also added cg_syscalls.c to build (missing in + Win32). + +2000-12-01 Bernd Kreimeier + + * RC3: released as of sos001201 / Q3 1.27f + + * code/unix/Makefile: more fixes with clean build. The + changes made fix the menu rendering for Q3 but not TA. + Ingame graphics still broken. + + * code/game/game_ta.sh: created. Use game_ta.q3asm here. + * code/game/game.sh: no -DMISSIONPACK + * code/game/game_ta.q3asm: CR/LF, /. + + * code/cgame/cgame_ta.sh: created. Use cgame_ta.q3asm here. + * code/cgame/cgame.sh: no -DMISSIONPACK. No cg_newdraw, ui_shared. + * code/cgame/cgame.q3asm: No cg_newdraw, ui_shared. + CR/LF, /, cg_newDraw, and the output path/name. + + * code/q3_ui/q3_ui.q3asm: output to ui not q3_ui... + + * code/cgame/cg_event.c: cg_singlePlayerActive used here. + TODO: guard by MISSIONPACK + * code/cgame/cg_local.h: named q3print_t enum. Cvar + cg_singlePlayerActive for both Q3 and TA. + + + * code/unix/Makefile: cleanly separate B/baseq3/ and + B/missionpack/ subtrees during build. While new and old + UI are in separate directories, the cgame/ and game/ + are shared, with conditional -DMISSIONPACK compile + and different files includeds (cd_draw, cg_newdraw). + That means twice the number of targets (3 DLL's, 3 QVM's, + times two), and different build rules. + TODO: carefully check Win32 build for (other) conditionals + TODO: carefully check Win32 build for link lists + + * CVS: ui/, code/ui, botfiles/ and subdirectories are added. + The code/macosx/ directory turned out to be a real pain that + had to be edited manually, throwing out CVS/ directories in + the tree that had been created by SOS as they are in id's + repository: + code/macosx/Client/CVS + code/macosx/Client/PBUserInfo/CVS + code/macosx/Client/Quake3.nib/CVS + code/macosx/Common/CVS + code/macosx/DedicatedServer/CVS + code/macosx/DedicatedServer/PBUserInfo/CVS + Now tagged + cvs tag id1-26y-loki001119 + TODO: there are several new files not yet linked? + + * ChangeLog: merged the Changelog from the bk00119 working + branch (initial Q3TA port) based on sos001119 snapshot. Also + merged the source tree with cvs-1.17. + In the ChangeLog below *** MISSIONPACK *** indicates work + that was done on the branch (code-sos/ prefix in files). + The cvs update of this will be tagged with + cvs tag id1-26y-loki001119 + Use this tag to hunt for possible Linux fixes that got lost + (i.e. got dropped by id since id000516 and were thus not in + sos001119, but did not show in diff id000516 cvs1-17). + New directories in CVS: botfiles/, ui/. + Missing from SOS/Missionpack: SDK directories. + common, lcc, libs, q3asm, q3data, q3map, q3radiant. + + + * ssreport.txt: below. + Note: watch for files called "ssreport.txt", that's id ChangeLog. + * ui/ui_syscalls.asm: below. + * q3_ui/ui_syscalls.asm: below. + * game/g_syscalls.asm: below. + * cgame/cg_syscalls.asm: below. + * bspc/linux-i386.mak: below. + * bspc/lcc.mak: below. + * botlib/linux-i386.mak: below. + * botlib/lcc.mak: below. + * A3D/a3d_console_variables.txt: CR/LF issue (minimize diffs). + + * CVS: the checked bk001119 work copy of the sos001119 initial + checkout (completed with everything in the SOS "Missionpack" + tree, i.e. botfiles/ and botfiles.* added), copied over the + cvs-1.17 checkout. + Note: in these cases, BEWARE ui -> q3_ui/ links, and different + ChangeLogs. Also "make clean" helps. + + * unix/unix_net.c: below. + * unix/unix_main.c: below. + * unix/matha.s: below. + * unix/linux_qgl.c: below. + * unix/linux_glimp.c: see also linux_joystick.c. + * server/sv_client.c: below. + * renderer/tr_surface.c: below. + * renderer/qgl.h: below. + * qcommon/qcommon.h: below. + * qcommon/files.c: below. + * qcommon/common.c: below. + * q3_ui/ui_demo2.c: below. + * mac/mac_net.c: below. + * mac/mac_glimp2.c: below. + * game/surfaceflags.h: below. + * game/bg_lib.c: checked against id00516/cvs-1.17a diff. + * bspc/bspc.c: TH_AASToTetrahedrons call removed since id000516. + Note: our final compare of id000516 against cvs-1.17a, making sure + that all these differences are in bk001119 (initial Q3TA port). + If id branched the Q3TA base off before id000516 we might be screwed. + Note: I do not diff against bk000520, which had some minor changes + against id000516 (check VectorArrayNormalize, OTConfiguration), which + seem consistent with me taking a pre-id000516 source snapshot for that + working branch. + +2000-11-30 Bernd Kreimeier + + * TEST: compiled using the symbolic link ui/ -> q3_ui/. + Had to undo one CVS change, regarding + code/cgame/cg_syscalls.asm + code/game/g_syscalls.asm + code/q3_ui/ui_syscalls.asm + These files are neither generated by Win32 cgame.bat + nor cgame.sh (etc.), thus seemingly maintained by hand. + cvs tag pr1-17-loki001130b + should be used if somebody needs this 1.17 snapshot + (which, remember, is post-release, with additional fixes). + Later: + cvs tag pr1-17-loki001130c + includes the full ChangeLog (duh). + + * CVS: up until cvs-1.17-001130, code/ui/ contained the + Q3 code for the UI QVM/DLL. In Q3TA, this code has been + moved to code/q3_ui/, while at the same time the new + (scripting driven) UI code for Q3TA was maintained in + code/ui/. To preserve the history of code/ui/, it has been + renamed to q3/ui/ in the CVSROOT. + Note: this will BREAK all cvs-1.17 and before checkouts. + To compile earlier versions, move or link q3_ui/ to ui/. + The code has been tagged + cvs tag pr1-17-loki001130 + after the change. + DONE: remove code/*/vm/*.asm from CVSROOT + Note: this includes code/*/*.asm files (from *_syscalls.c). + These were originally tracked in CVS, but if we need + comparison of q3asm output or QVM files we can rely + on the Win32 and Linux SDK now. These files have been + physically removed from CVS now, followed by + cvs tag pr1-17-loki001130a + +2000-11-30 Bernd Kreimeier *** MISSIONPACK *** + + * RC2: new ZIP file (another 360M for convenience). + + * SOS: new CVS module, Quake3_sos. This will be used to track + the unchanged SOS checkouts from id. As their repository + is read-only, and there is no estimate on when changes might + be backpropagated there, I will track their changes in a + separate module, and update our local Quake3 module + accordingly. This is effectively "tracking 3rd party" + w/o import and half-automated, forced mergers - in other + words, we now branch starting with our post-1.17 changes, + for the benefit of moving at all. + Baseline is a slightly changed PR-1.17 id000516 source dump + (essentially ui/ moved to q3_ui for continuity, and CR/LF etc.). + cvs import Quake3_sos id000516 pr1-17-win32 + cvs import Quake3_sos sos001119 pr1-26-win32 + cvs import Quake3_sos sos001120 pr1-26-win32-001120 + cvs import Quake3_sos sos001121 pr1-26-win32-001121 + cvs import Quake3_sos sos001122 pr1-26-win32-001122 + cvs import Quake3_sos sos001122-demo pr1-26-win32-demo + This is about the 1.26w Team Arena Win32 demo release, give or + take a couple of lines. Has Q3_VERSION "Q3 Team Arena Demo 1.27b". + cvs import Quake3_sos sos001123 pr1-26-win32-001123 + cvs import Quake3_sos sos001126 pr1-26-win32-001126 + Now track id versions (see code/game/q_shared.h:Q3_VERSION) + cvs import Quake3_sos sos001128 pr1-27c-win32-001128 + With 1.27d they switched from Demo to full version (RC1). + cvs import Quake3_sos sos001129 pr1-27d-win32-001129 + cvs import Quake3_sos sos001130a pr1-27d-win32-001130a + Now switched to 1.27e. This import is done from the SOS + working directory. + cvs import Quake3_sos sos001130b pr1-27e-win32-001130b + Note: SoS created rwx attributes which are luckily fixed + automagically during import. It is also seemingly incapable + to compare files, and leave files that have not changed the + hell alone. I can't do cvs update due to the $..$ tags in + the original files (which CVS can't be told to ignore), + so I have to do import (creating a load of vendor tagged + branches), but at least cvsweb and cvs get the revisions + right. + + * code-sos/unix/Makefile: added linux_joystick + * code-sos/unix/linux_local.h: match mac/ and win32/, for prototypes. + + * code-sos/unix/linux_joystick.c: new file, code from linux_glimp.c + Note: decided to separate this, as (a) we might edit/extend + a lot, (b), it's not in the id tree, (c) it's not GL, (d) + there might be even more oddball devices. Anything that + cuts down on diffs. + + * code-sos/unix/linux_glimp.c (Q_stristr): const return (cvs1.17). + Also (XLateKey): added more keyboard mappings (ASCII on + upper row digits) (cvs1.17). Added in the minimal joystick + hooks (cvars, function calls). Fixed joystick cvar naming + to match win32 (kept joystick_threshold). + TODO: joystick stubs for dedicated? + + * CVS: I have to move up to 1.27d (data, Win32 networking). + With exception of linux_glimp.c (mostly joystick code), + all cvs1.17 changes should now be in the work snapshot + based on the first sos001119 we got from id. There are + also some additional changes in there already, thus I'll + move the (buggy) 1.26 snapshot into CVS before adding even + more differences. + + +2000-11-29 Bernd Kreimeier *** MISSIONPACK *** + + * RC1: TeamArena_Q3A_RC1.zip. Source has moved from + Q3VERSION "Q3 Team Arena Demo 1.27c" to "Q3 1.27d" now. + + * code-sos/qcommon/common.c: added Com_InitPushEvent(). Also + increased MAX_PUSHED_EVENTS to 256. + Note: this is another case of buffer memory not zero'ed. + Com_EventLoop, fixed evTime to evType in debug print. + + * TEST: baseq3/ + +set sv_pure 0 +set vm_game 0 +set in_mouse 0 +set developer 2 + TODO: Team Arena in menu leads to RE_Shutdown(1) and locks + TODO: can't play game + TODO: shaders can't load *.tga, *.jpg files are there + TODO: DO_CC linking for DLL's, DO_SHLIB_CC only for export? + TODO: ERROR: Bad player movement angle + TODO: Warning: cvar "..." given initial values: "..." and "..." + TODO: TA demo ERROR: CL_ParseServerMessage: Illegible server message + TODO: WARNING: Com_PushEvent overflow + + * code-sos/qcommon/files.c: add NULL filter for our Sys_ListFiles calls. + * unix/unix_shared.c (Sys_ListFiles): signature has changed, + additional Sys_ListFiles argument now. + + * code-sos/unix/unix_net.c (Sys_GetPacket): see below (readcount=0). + * code-sos/unix/unix_main.c: see below (Mike's and my changes to DLL + loading, my event buffer clear fixes). + * code-sos/unix/linux_qgl.c (QGL_Init): see below (__FX__ guards). + TODO: abstract WGL/GLX and end unfortunate QGL duplication. + TODO: spice up QGL with Linux H2 full version. + * code-sos/q3_ui/ui_demo2.c: fix on demo names - no Q_strupr(demoname). + Note: in CVS this fix is in ui/ui_demo2.c. CVS is screwed by + id choosing the old name for new directory... + TODO: manual intervention on "ui goes q3_ui" in CVSROOT? + * renderer/qgl.h: see below (__FX__ guards). + * qcommon/files.c: migrated in the 1.17cvs changes against the + id000516 code dump, i.e. the (not marked - boo hiss) mkv changes. + Note: all the above is based on a diff of the last id code dump + pre-1.17 against our CVS, with those fixes now migrated into the + sos1.26 snapshot. + TODO: move in joystick code. + TODO: replace XAutoRepeatOn/Off with filter (focus). + TODO: DGA 2.0 and such. + + * code-sos/game/q_shared.c: valid compare for NULL strings + * code-sos/unix/unix_main.c: QRTLD, and now using RTLD_NOW. + Note: it is a bad idea to load game DLL's that are missing symbols. + + * code-sos/ui/ui_main.c: see below. + * code-sos/game/g_main.c: see below. + * code-sos/q3_ui/ui_main.c: see below. + * code-sos/cgame/cg_main.c: made cvarTable and cvarTableSize static. This resolved + a segfault related to traversing the UI table during Init. + Note: there is a segfault related to this variable being out of bounds. + Different struct size in global variables possible aliasing between the + DLL's. + + * code-sos/unix/unix_main.c (Sys_Error): assert(0), no exit in debug. + * code-sos/game/q_shared.c: now aborts on NULL destination. Also DPrintf's + on bogus excess copies. + TODO: make all those string functions safe, at least assert. + * code-sos/server/sv_init.c: comment in SV_Init + // init the botlib here because we need the pre-compiler in the UI + Called in qcommon/common.c:Com_Init, were CL_Init is called afterwards... + * code-sos/server/sv_bot.c: the botlib_import is filled here. + * code-sos/unix/unix_main.c (Sys_GetBotLibAPI): RTLD_NOW. Which is for naught, + as this code is not used and has never been used. assert(0) + + * code-sos/botlib/be_interface.c: botimport supposed to be set here. + * code-sos/botlib/l_memory.c: segfault with q3_ui/ DLL. + #1 0x80e23ec in GetMemory (size=35) at ..//botlib/l_memory.c:331 + 331 ptr = botimport.GetMemory(size + sizeof(unsigned long int)); + as botimport is completely NULL'ed. + + * code-sos/q3_ui/q3_ui.sh: created from ui/ui.sh 1.17 + + * code-sos/q3_ui/q3_ui.q3asm: unfubared (CR/LF, / path). + + * code-sos/unix/Makefile: added q3_ui/ make targets (basically + ui/ targets from CVS 1.17 Makefile for starters). + + * code-sos/q3_ui/: this is the old UI code, which does not use + ../ui/menus.txt (see ui/ui_main.c). In other words, + the code in ui/ now has to be compiled or qvm'ed + for missionpack/, but to create the necessary DLL or + QVM modules for baseq3/ we need to use q3_ui/. + + +2000-11-27 Bernd Kreimeier *** MISSIONPACK *** + + * code-sos/game/bg_lib.c: ld problem with a custom "tan(..)" + TODO: loooking forward to SIGFPE on this code base. + + * code-sos/ui/ui_util.c: this file is empty. + + * code-sos/ui/ui.sh: new files: + ui_shared.c + ui_util.c + Replaced by the /ui/*.menu files: + q3lcc: can't find `../ui_cdkey.c' + q3lcc: can't find `../ui_ingame.c' + etc. + + * code-sos/cgame/cgame.q3asm: added cg_newdraw entry. + Also added ui_shared entry. + * cgame/cgame.sh: added cg_newdraw.c entry. + Also added ../ui/ui_shared.c entry. + + * code-sos/cgame/cg_newdraw.c: renamed (was cg_newDraw.c mixed case). + Note: the infidels have taken over. + + * cgame/cgame.sh: added -DMISSIONPACK. + Note: w/o, q3lcc complains + ../cg_event.c:204: undeclared identifier `cg_singlePlayerActive' + ../cg_event.c:204: left operand of . has incompatible type `int' + which indicates that this source does not compile w/o MISSIONPACK + anymore. The baseq3/pak4.pk3 file in the Q3TA snapshot archives + are dated + 284464 11-10-00 14:02 vm/cgame.qvm + 463940 11-14-00 14:47 vm/qagame.qvm + 271596 11-14-00 14:48 vm/ui.qvm + the code dump is from 11-19. + Note: Make does not abort on q3lcc complains + + * code-sos/game/game.sh: also added ai_vcmd.c entry. + + * code-sos/ui/ui.q3asm: fubared (below). In addition, this is + the only one to have a + -o "/tmp/quake3/missionpack/vm/ui" + line in it. Given that the other 2 QVM modules are + also dependend on -DMISSIONPACK, this seems a real mess. + For now using the same path as the other 3. + * code-sos/cgame/cgame.q3asm: below. + * code-sos/game/game.q3asm: fubared. Fixed CR/LF and \ in paths + again (read by q3asm called by game.sh called by make). + * unix/Makefile: updated fpor DLL/QVM. + Note: also shell scripts to use q3lcc not lcc. + + +2000-11-27 Bernd Kreimeier + + * code/unix/Makefile: now expects a run/ directory + relative (between this, the Loki standards, and the + utility code in the same repository, it's ever so + slightly less dorky). + TODO: fix broken copyfiles target etc.pp. + + * code/game/bg_lib.c: turns out the changes I + undid 001120 were affecting original Zoid + Linux port related defines, which break VM + compile. Mike fixed those (which I unfixed + when referring to the latest id code that does + not contain these patches). However, they + duplicate ANSI libc symbols, so the guards might + be wrong. The symbols are missing when compiling + for VM, so I now use the existing lcc -DQ3_VM + flag: + //#if !defined ( _MSC_VER ) && ! defined ( __linux__ ) + #if defined ( Q3_VM ) + This will break DLL compile on non-ANSI platforms, + which will have to be added to the conditional then. + + * code/ui/ui.sh: below. + * code/game/game.sh: below. + * code/cgame/cgame.sh: Linux SDK installs q3lcc to + avoid collisions with regular lcc pre-installs. The + scripts fail with "lcc not found", but do not abort + the Makefile. + Note: now that VM code gets actually built, there + are errors: + g_main:648 ERROR: symbol vsprintf undefined + bg_pmove:1221 ERROR: symbol abs undefined + q_math:4309 ERROR: symbol fabs undefined + q_shared:2801 ERROR: symbol tolower undefined + q_shared:2862 ERROR: symbol toupper undefined + ai_dmq3:208 ERROR: symbol atoi undefined + ai_cmd:4951 ERROR: symbol sscanf undefined + + +2000-11-20 Bernd Kreimeier + + * TEST: test compile of pr-1.17+cvs fixes segfaults due + to new baseq3/pak4.pk3 + Note: to self ... 1.17 is not compatible with new files. + Checking into CVS next. + + * code/: changes applied by us that are not in id's code base + affect q_shared.c (NULL in Q_stricmp), files.c (FIXME fs_cdpath, + Sys_ConcatenateFileList, ui_demo2.c (demo no tolower on linux). + In unix/ linux_glimp.c (joystick code), qgl.h, linux_qgl.c (__FX__), + unix_main.c (dlopen bug and event buffers), unix_net.c (readcount), + matha.s (assembly warning). + + * code/server/sv_client.c (SV_WriteDownloadToClient): + No effective change on FS_SV_FOpenFileRead call, they reworked + autodownload some more seemingly. + + * code/renderer/tr_surface.c: VectorArrayNormalize + + * code/qcommon/qcommon.h: see below. + * code/qcommon/files.c: Com_ReadConfigs removed. + * code/qcommon/common.c: removed Com_ReadConfigs, + textual replacement of body in Com_Init. + + * code/mac/mac_net.c: not applied (undone by id) + OTConfiguration *config <> OTConfigurationRef config + + * code/mac/mac_glimp2.c: r_colorbits->integer > 16 + * code/game/surfaceflags.h (CONTENTS_BOTCLIP): added. + + * code/game/q_shared.h: not applied (undone by id) + #if defined(ppc) || defined(__ppc) || defined(__ppc__) + #define idppc 1 + #else + #define idppc 0 + #endif + + * code/game/q_math.c: added another CPP line to guard + BoxOnPlaneSide, removed WIN32 guard. + TODO: this could be broken code guarded in all current + compiles... + + * code/game/bg_lib.c: left Q#_VM guard for typedef cmp_t + Added !defined( __linux__ ) for tolower and atoi. + Note: the changes above relate to the very last code update + from id prior to the 6 month blackout, which were not in + CVS when Michael made his updates. Needed to establish the + baseline for the new patch. Source dump 1.17.00520, against + SOS 1.26w-001119 version. + +2000-11-20 Bernd Kreimeier *** MISSIONPACK *** + + * TEST: running against the data up to TeamArena_Q3A_001109.zip + Hunk_Clear: reset the hunk ok + Program received signal SIGBUS, Bus error. + "q3dm2", killBots==qtrue + #0 CM_ClearMap () at ..//qcommon/cm_load.c:644 + #1 0x80884a7 in SV_Map_f () at ..//server/sv_ccmds.c:159 + #2 0x8072579 in Cmd_ExecuteString (text=0xbffff4b0 "spmap q3dm2") at ..//qcommon/cmd.c:591 + #3 0x8071dfe in Cbuf_Execute () at ..//qcommon/cmd.c:190 + #4 0x80763f7 in Com_Frame () at ..//qcommon/common.c:2547 + #5 0x8130d6b in main (argc=13, argv=0xbffff984) at ..//unix/unix_main.c:953 + #6 0x40100cb3 in __libc_start_main (main=0x8130bc4
+ Not reproducible (screen stayed black). + + * TEST: +set developer 1, same for Win32 and Linux: + Can't find gfx/misc/flare.tga + Can't find gfx/misc/sun.tga + Can't find gfx/misc/console02.tga + Can't find vm/ui.map + Can't find textures/sfx/logo512.tga + Can't find gfx/colors/black.tga + Can't find models/mapobjects/banner/banner5_2.md3 + Can't find models/mapobjects/banner/banner5_1.md3 + Can't find textures/sfx/firegorre2.tga + Can't find textures/sfx/bolts.tga + Can't find menu/art/unknownmap.tga + + * Q3TA: after nearly 6 months, a code update from id. SOS access + even. Got it to compile, link and start, but its currently broken + (menu doesn't render in full, can't get into game etc.). Need + a baseline 1.17 to diff against. Last code dump was May 16, with + bspc code updated May 19. Checking working directory of bk000520 + against CVS next (Mike's fixes never made it into id's codebase + or a post 1.17 release, neither did my fixes as released in the + point release version 1.17). + +2000-11-19 Bernd Kreimeier *** MISSIONPACK *** + + * TEST: Win32 install as tested with 1.26w. quake3.x86 (Q3A game) + Warning: cvar "r_uifullscreen" given initial values: "1" and "0" + Warning: cvar "r_inGameVideo" given initial values: "1" and "0" + ^3WARNING: sound/feedback/hit.wav is a 8 bit wav file + (on windows, sound/weapons/weapon_hover.wav is missing...) + Menu only partially displayed in TA and baseq3 play, menu itself + seems to work. Freetype? + WARNING: Com_PushEvent overflow + + * code-sos/game/game.sh: not in SOS, moved in from CVS snapshot. + + * code-sos/qcommon/common.c: conditional DEDICATED to get rid off + CL_ShutdownCGame/CL_ShutdownUI/CIN_CloseAllVideos. + Same for UI_usesUniqueCDKey: dedicated server does not + write CD key file. + TODO: check whether there is an unneeded "read CD key" + for dedicated server. + + * code-sos/null/null_client.c (CL_ShutdownAll): added dummy. + + * code-sos/unix/Makefile: server/sv_net_chan.o for dedicated server. + + * code-sos/null/null_snddma.c: fixed S_RegisterSound signature. + + * code-sos/client/snd_mix.c: snd_p, snd_linear_count, snd_out + can't be static, as used by unix/snd_mixa.s. + + * code-sos/unix/Makefile: added to the executable target: + renderer/tr_font.c + client/cl_net_chan.c + server/sv_net_chan.c + Also added a lot of jc*.c files to build, to fix unresolved + symbol errors. + TODO: is there unused jpeg-6/jd*.o code linked in now? + + * code-sos/ft2/smooth.c: includes ftgrays.c, ftsmooth.c + + * code-sos/ft2/truetype.c: ttdriver.c, ttpload.c, ttgload.c, ttobjs.c. + Also (see ftoption.h) TT_CONFIG_OPTION_BYTECODE_INTERPRETER ttinterp.c + + * code-sos/ft2/sfnt.c: includes ttload.c, ttcmap.c, sfobjs.c, + sfdriver.c. lso (see ftoption.h) + TT_CONFIG_OPTION_EMBEDDED_BITMAPS ttsbit.c + TT_CONFIG_OPTION_POSTSCRIPT_NAMES ttpost.c + + * code-sos/ft2/ftbase.c: includes ftcalc.c, ftobjs.c, ftstream.c, + ftlist.c, ftoutln.c, ftextend.c, ftnames.c. + + * code-sos/ft2/autohint.c: includes ahangles.c, ahglyph.c, ahglobal.c, + ahhint.c, ahmodule.c. + + * code-sos/unix/Makefile: added ft2/ to client objects, took out + ftraster.c/ftrend1.c (see below), added -DFT_FLAT_COMPILE. + * ft2/ftsmooth.c: -DFT_FLAT_COMPILE required. + * ft2/raster1.c: -DFT_FLAT_COMPILE required. + Note: this includes ftraster.c/ftrend1.c. + + * code-sos/qcommon/vm_x86.c: _ftol is missing, ftolPtr only defined + for Win32, but used in generic code. Workaround for now. + TODO: find good Linux ftol, or use old solution. + + * SoS checkout. chown -R a+w * recode ibmpc:lat1 */*.h */*.c + +2000-06-30 Michael Vance + + * misc: Spoke with Leonardo about qvm mess. + + * ui/ui.sh: Created to build much like the ui.bat script. + + * ui/ui.q3asm: Use linux style paths. + + * game/game.sh: Created to build much like the game.bat script. + + * game/game.q3asm: Use linux style paths. + + * cgame/cgame.sh: Created to build much like the cgame.bat script. + + * cgame/cgame.q3asm: Use linux systel paths. + + * unix/Makefile: Use the new .sh scripts to build the QVM files. + + * lcc/etc/linux.c: Build .asm files instead of .s files. + + * misc: QVMs now load properly, with minor glitches that should + hopefully be solvable. The new build scripts conflict with the + .asm files already in CVS, as the generated byte code is slightly + different in some cases. + +2000-06-29 Michael Vance + + * lcc/makefile: Tweaked to automatically include the system + compiler's header location. Added an install directory. + + * lcc/custom.mk: Added a build directory. + + * lcc/etc/linux.c: Numerous small tweaks to make compiling the VM + code a much simpler task. + + * q3asm/Makefile: Created. + + * q3asm/q3asm.c: Fixed uninitialized variable in + HashString(). Fixed off by one in argument parsing. + + * misc: Had Brian remove the Xmd.h include from glx.h so that we can + build Quake3 on XFree86 4.0 systems. + + * wine: Attempted to build with lcc.exe and q3asm.exe using wine, + also did not work. This is in contrast to MikeP's .qvms, which + seem to work. + +2000-06-28 Michael Vance + + * common/files.c: Fixed Mods menu behaviour. + + * unix/linux_qgl.c: Guarded references to fxMesa. + + * renderer/qgl.h: Guarded references to fxMesa. + + * ui/ui_demo2.c: Don't convert filename to uppercase. + +2000-05-07 Bernd Kreimeier + + * common/cmdlib.c: windowism, not guarded. Added WIN32 around "ATOM a". + + * q3map/Makefile: Linux Makefile. + + * q3map/Makefile.irix: "makefile" in original code, Irix-only Makefile. + Just fixed some redundant TAB that GNU make despises about as much as I + despise GNU Make, and changed to a relative path. + +2000-05-01 Bernd Kreimeier + + * q3radiant/: updated with Q3Radiant198b3-src.zip. + Tagged (globally) as q3radiant-198b3. + Kept the old files + 3DFXCamWnd.h + 3DFXCamWnd.cpp + MainFrm2.cpp + New files + Shaders.h + misc/ (contributed special TGA resources, don't relly belong) + Removed: + pName + Changed filenames to previous case: + UNNAMED.MAP -> unnamed.map + RES/BMP0002.BMP -> RES/bmp00002.bmp + Changed: + changelog.txt -> ChangeLog + +2000-04-28 Bernd Kreimeier + + * CVS: bk000425 modified sources. This replaces the unix/ directory + which is not yet in id's SourceSafe. Two check-ins, due to minor + changes in an attempt to nail the Voodoo3 related crashes (driver + problems, not a Q3 issue). Undid some of the QFL changes for PI + and the log bug fix - put back in (TODO). Also includes: + * Quake3/code/botlib/be_aas_sample.c: single file update from Robert. + + * CVS: id000423 code dumps (two of them). Applying Loki patches. + Tagged for the final version (all patches). + + * CVS: id000422 code dump. This did not include the 1.16n fixes + used for Linux, and was the first dump for the 1.17 security fix + release. + Note: forgot to check in the ft2/ headers themselves, but they + are not used in the current codebase anyway. Are added in next + dump. Also there is use of CVS/CVS-like $Keyword$ patterns in + some files, and between their revisions and ours we fuck this up. + Also, id ZIP files create write protected sources, have to do + chmod -R a+w Quake3/ to work and overwrite files. + + * CVS: bk000315 modified source. This version was the 1.16n release. + Note: the changes applied here are not in the subsequent code dumps + of id. If you want to compile the Linux version as released you + have to use bk-tagged versions until the patches are merged in by + Robert Duffy. + + * CVS: id000314 engine code dump, same procedure as below, tag. + Note: this version added vm/ sudirectories with assembly files + for cgame, game, ui. CVS tag id000314. + + * CVS: id000304 engine code dump. Now there is a problem, as CVS + was used in the Mac sources. Do + find . -name 'CVS' -exec rm -r {} \; + before cvs update, then tagged: + cvs -d /loki/cvsroot/ tag id000304 Quake3/ + + * CVS: checked in a source snapshot of the id00303 engine code + and the id0003029 tools code. The tool sources are not fully in + sync, and we have only partial source from earlier engine revisions. + The engine source marks where Loki took over from Dave Kirsch. + This snapshot (with all temporary and bogus files) is imported + and tagged using: + cvs -d /loki/cvsroot import Quake3 id000303 initial + + Modules: + code: the Q3 engine code, including a jpeg-6/ copy + common: code shared by tools + libs: code shared by tools, inlcuding a jpeg6/ copy + q3asm: VM bytecode assembly + q3data: misc. Q3 data conversions + q3map: BSP builder + q3radiant: Win32 editor, as is + lcc: C compiler for q3asm + + The sources have not been cleaned up, and binary files have not been + removed. The Q3Radiant code base might exhibit mixed case asmbiguities + in the future, and future source dumps might come from SourceForge + instead. + +2000-04-25 Bernd Kreimeier + + * q3code.id000425/unix/Makefile: relative path, relocatable. + Note: first code merge with id, finally :-). + +2000-04-24 Bernd Kreimeier + + * q3code.bk000422/unix/matha.s: in C(BoxOnPlaneSide) + the following line triggers assembler warning: + "missing prefix `*' in absolute indirect address, maybe misassembled!" + jmp Ljmptab(,%eax,4) + + + * q3code.bk000422/unix/Makefile (MOUNT_DIR): rember to change. + TODO: fix this bloody Makefile to be relocatable, damnit. + + * q3code.bk000422/cgame/cg_event.c: applied JCash fix again + (see EV_EVENT_BITS below). Send e-mail to verify. + + * q3code.bk000422/renderer/tr_image.c: "../jpeg-6/jpeglib.h" again. + + * q3code.bk000422/: created from the id dump of today, lacking + all but one of my changes (sigh). Swapped unix/ competely, takes + care of 90%. Submitted all changes again to Robert... + +2000-04-19 Bernd Kreimeier + + * q3code.bk000315/unix/linux_glimp.c (GLimp_EndFrame): + QGL_EnableLogging( r_logFile->value ) doesn't work? + + * q3code.bk000315/unix/linux_qgl.c: GLimp_LogNewFrame() is + obsolete. QGL_EnableLogging was out of sync with Win32 and + did not support the new framecounter decrement logic. + +2000-04-03 Bernd Kreimeier + + * q3code.bk000315/server/sv_snapshot.c: svs.nextSnapshotEntities + is a signed integer unconditionally incremented, which gets + negative and causes a segfaulting indexing an array. Added reset + to counter. Might fail if snapshot numbers are supposed to + monotonically increase. + +2000-04-02 Bernd Kreimeier + + * q3code.bk000315/client/cl_parse.c (CL_ParseServerMessage): + assert(0) on Illegible message (remember to +set in_mouse 0). + TODO: have to add a dump message function, it's unreadable. + + * botlib/be_ai_goal.c (InitLevelItemHeap): loop counter -2 + left -2 with uninitialized next, and -1 disconnected. Removed + redundant memset. There is an item alloc leak I suspect, as + max_levelitems 1024 merely delayed the overflow error. + +2000-04-01 Bernd Kreimeier + + * botlib/be_ai_goal.c (InitLevelItemHeap): still segfaults. + Not memsetting the entire item heap. As items are cleared + on return, that leaves only memory corruption? + Later: upped max_levelitems from 256 to 1024 + Later: client dies on connect: + Error: CL_ParseServerMessage: Illegible server message 255 + + +2000-03-31 Bernd Kreimeier + + * botlib/be_ai_goal.c: initializing global vars. + Segfault in AllocLevelItem () + at /home/bk/Games/Quake3/q3code/botlib/be_ai_goal.c:364 + I suspect that the initial freelevelitems setting is at + the end of the list and eventually exposed. + + * cgame/cg_event.c: according to Johmn Cash: + itemNum = (es->event & ~EV_EVENT_BITS) - EV_USE_ITEM0 + Quote: "This causes itemNum to be invalid about half the time, + preventing any client side effect tied to the item from occurring." + +2000-03-06 Bernd Kreimeier + + * qcommon/common.c: set pushEvent buffer and indices + to zero in Com_Init(). + + * q3code/qcommon/qcommon.h: made SE_NONE (and for paranoia + also NA_BOT) explicitely set to zero. + +2000-02-27 Bernd Kreimeier + + * unix/Makefile: added dmalloc in an attempt to get on + the Z_Free bug. Futile. Despite stripping dmalloc debug + token down to essentials, I get a (seemingly bogus or + unrelated): + debug-malloc library: dumping program, fatal error + Error: possibly bad .c filename pointer (err 24) + + +2000-02-26 Bernd Kreimeier + + * qcommon/common.c: various debug builts to isolate the + Z_Free bug. It reproducibly happens on some machines + with SE_PACKET, but the packets themselves look + thoroughly corrupted. + +2000-02-21 Bernd Kreimeier + + * qcommon/common.c (Com_EventLoop): possible problem + here, pointer does not get cleared. + + * unix/linux_glimp.c (InitSig): no signal handler. + * common/common.c: dump in Com_Error for debug. + +2000-02-17 Bernd Kreimeier + + * q3code: new dump from Zoid. Repeat tr_image.c fix. + + * unix/Makefile: added client/snd_adpcm.c (linkage errors). + Later: added entire JPDIR and rules, for tr_image.c. + Later: had to fix fules for game/ai_*.c files. + Later: removed ui/ui_quit.o (n/a) + Later: took out -mpentiumpro -march=pentiumpro + + * renderer/tr_image.c: windowism in #include path (see below). + #include "..\jpeg-6\jpeglib.h" + +1999-12-27 Bernd Kreimeier + + * Alpha: tried a dedicated server compile. Segfaults in + ../qcommon/files.c:1682, a paksort function doing pointer + fiddling. + + * Makefile.alpha: created. + Note: want to take the SDL/Setup autoconf ASAP. + + * unix/unix_main.c: fixed __axp__ to __alpha__, guarded + _FPU_SETCW. + + * qcommon/vm_alpha.c: dummy, created. + * qcommon/vm_null.c: dummy, created. + +1999-12-04 Bernd Kreimeier + + * renderer/tr_image.c: windowism in #include path. + #include "..\jpeg-6\jpeglib.h" + + * Revision 1.11: from Zoid by e-mail. + Note: threw away my playground copy, starting with the + ZIP file. Zoid's using CVS now, but we can't remote + access it. Thus did the + "find . -name 'CVS' -exec rm -rf {} \;" + and then track it as 3rd party source by + + + * ChangeLog: created. Now starting to track Q3A source. + +--------- q3code log --------------------------------------------- diff --git a/reaction/engine/Makefile b/reaction/engine/Makefile new file mode 100644 index 00000000..e1d8f335 --- /dev/null +++ b/reaction/engine/Makefile @@ -0,0 +1,2176 @@ +# +# ioq3 Makefile +# +# GNU Make required +# + +COMPILE_PLATFORM=$(shell uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]') + +COMPILE_ARCH=$(shell uname -m | sed -e s/i.86/i386/) + +ifeq ($(COMPILE_PLATFORM),sunos) + # Solaris uname and GNU uname differ + COMPILE_ARCH=$(shell uname -p | sed -e s/i.86/i386/) +endif +ifeq ($(COMPILE_PLATFORM),darwin) + # Apple does some things a little differently... + COMPILE_ARCH=$(shell uname -p | sed -e s/i.86/i386/) +endif + +ifeq ($(COMPILE_PLATFORM),mingw32) + ifeq ($(COMPILE_ARCH),i386) + COMPILE_ARCH=x86 + endif +endif + +ifndef BUILD_STANDALONE + BUILD_STANDALONE = 1 +endif +ifndef BUILD_CLIENT + BUILD_CLIENT = 1 +endif +ifndef BUILD_CLIENT_SMP + BUILD_CLIENT_SMP = 1 +endif +ifndef BUILD_SERVER + BUILD_SERVER = 1 +endif +ifndef BUILD_GAME_SO + BUILD_GAME_SO = 0 +endif +ifndef BUILD_GAME_QVM + BUILD_GAME_QVM = 0 +endif +ifndef BUILD_MISSIONPACK + BUILD_MISSIONPACK= 0 +endif + +ifneq ($(PLATFORM),darwin) + BUILD_CLIENT_SMP = 0 +endif + +############################################################################# +# +# If you require a different configuration from the defaults below, create a +# new file named "Makefile.local" in the same directory as this file and define +# your parameters there. This allows you to change configuration without +# causing problems with keeping up to date with the repository. +# +############################################################################# +-include Makefile.local + +ifndef PLATFORM +PLATFORM=$(COMPILE_PLATFORM) +endif +export PLATFORM + +ifeq ($(COMPILE_ARCH),powerpc) + COMPILE_ARCH=ppc +endif +ifeq ($(COMPILE_ARCH),powerpc64) + COMPILE_ARCH=ppc64 +endif + +ifndef ARCH +ARCH=$(COMPILE_ARCH) +endif +export ARCH + +ifneq ($(PLATFORM),$(COMPILE_PLATFORM)) + CROSS_COMPILING=1 +else + CROSS_COMPILING=0 + + ifneq ($(ARCH),$(COMPILE_ARCH)) + CROSS_COMPILING=1 + endif +endif +export CROSS_COMPILING + +ifndef COPYDIR +COPYDIR="/usr/local/games/Reaction" +endif + +ifndef MOUNT_DIR +MOUNT_DIR=code +endif + +ifndef BUILD_DIR +BUILD_DIR=build +endif + +ifndef GENERATE_DEPENDENCIES +GENERATE_DEPENDENCIES=1 +endif + +ifndef USE_OPENAL +USE_OPENAL=1 +endif + +ifndef USE_OPENAL_DLOPEN + ifeq ($(PLATFORM),mingw32) + USE_OPENAL_DLOPEN=1 + else + USE_OPENAL_DLOPEN=0 + endif +endif + +ifndef USE_CURL +USE_CURL=1 +endif + +ifndef USE_CURL_DLOPEN + ifeq ($(PLATFORM),mingw32) + USE_CURL_DLOPEN=0 + else + USE_CURL_DLOPEN=1 + endif +endif + +ifndef USE_CODEC_VORBIS +USE_CODEC_VORBIS=0 +endif + +ifndef USE_MUMBLE +USE_MUMBLE=1 +endif + +ifndef USE_VOIP +USE_VOIP=1 +endif + +ifndef USE_INTERNAL_SPEEX +USE_INTERNAL_SPEEX=1 +endif + +ifndef USE_LOCAL_HEADERS +USE_LOCAL_HEADERS=1 +endif + +############################################################################# + +BD=$(BUILD_DIR)/debug-$(PLATFORM)-$(ARCH) +BR=$(BUILD_DIR)/release-$(PLATFORM)-$(ARCH) +CDIR=$(MOUNT_DIR)/client +SDIR=$(MOUNT_DIR)/server +RDIR=$(MOUNT_DIR)/renderer +CMDIR=$(MOUNT_DIR)/qcommon +SDLDIR=$(MOUNT_DIR)/sdl +ASMDIR=$(MOUNT_DIR)/asm +SYSDIR=$(MOUNT_DIR)/sys +GDIR=$(MOUNT_DIR)/game +CGDIR=$(MOUNT_DIR)/cgame +BLIBDIR=$(MOUNT_DIR)/botlib +NDIR=$(MOUNT_DIR)/null +UIDIR=$(MOUNT_DIR)/ui +Q3UIDIR=$(MOUNT_DIR)/q3_ui +JPDIR=$(MOUNT_DIR)/jpeg-6b +SPEEXDIR=$(MOUNT_DIR)/libspeex +Q3ASMDIR=$(MOUNT_DIR)/tools/asm +LBURGDIR=$(MOUNT_DIR)/tools/lcc/lburg +Q3CPPDIR=$(MOUNT_DIR)/tools/lcc/cpp +Q3LCCETCDIR=$(MOUNT_DIR)/tools/lcc/etc +Q3LCCSRCDIR=$(MOUNT_DIR)/tools/lcc/src +LOKISETUPDIR=misc/setup +NSISDIR=misc/nsis +SDLHDIR=$(MOUNT_DIR)/SDL12 +LIBSDIR=$(MOUNT_DIR)/libs +TEMPDIR=/tmp + +# set PKG_CONFIG_PATH to influence this, e.g. +# PKG_CONFIG_PATH=/opt/cross/i386-mingw32msvc/lib/pkgconfig +ifeq ($(shell which pkg-config > /dev/null; echo $$?),0) + CURL_CFLAGS=$(shell pkg-config --cflags libcurl) + CURL_LIBS=$(shell pkg-config --libs libcurl) + OPENAL_CFLAGS=$(shell pkg-config --cflags openal) + OPENAL_LIBS=$(shell pkg-config --libs openal) + # FIXME: introduce CLIENT_CFLAGS + SDL_CFLAGS=$(shell pkg-config --cflags sdl|sed 's/-Dmain=SDL_main//') + SDL_LIBS=$(shell pkg-config --libs sdl) +endif +# Use sdl-config if all else fails +ifeq ($(SDL_CFLAGS),) + ifeq ($(shell which sdl-config > /dev/null; echo $$?),0) + SDL_CFLAGS=$(shell sdl-config --cflags) + SDL_LIBS=$(shell sdl-config --libs) + endif +endif + +# version info +VERSION=1.36 + +USE_SVN= +ifeq ($(wildcard .svn),.svn) + SVN_REV=$(shell LANG=C svnversion .) + ifneq ($(SVN_REV),) + VERSION:=$(VERSION)_SVN$(SVN_REV) + USE_SVN=1 + endif +else +ifeq ($(wildcard .git/svn/.metadata),.git/svn/.metadata) + SVN_REV=$(shell LANG=C git svn info | awk '$$1 == "Revision:" {print $$2; exit 0}') + ifneq ($(SVN_REV),) + VERSION:=$(VERSION)_SVN$(SVN_REV) + endif +endif +endif + + +############################################################################# +# SETUP AND BUILD -- LINUX +############################################################################# + +## Defaults +LIB=lib + +INSTALL=install +MKDIR=mkdir + +ifeq ($(PLATFORM),linux) + + ifeq ($(ARCH),alpha) + ARCH=axp + else + ifeq ($(ARCH),x86_64) + LIB=lib64 + else + ifeq ($(ARCH),ppc64) + LIB=lib64 + else + ifeq ($(ARCH),s390x) + LIB=lib64 + endif + endif + endif + endif + + BASE_CFLAGS = -Wall -fno-strict-aliasing -Wimplicit -Wstrict-prototypes \ + -pipe -DUSE_ICON $(SDL_CFLAGS) + + ifeq ($(USE_OPENAL),1) + BASE_CFLAGS += -DUSE_OPENAL + ifeq ($(USE_OPENAL_DLOPEN),1) + BASE_CFLAGS += -DUSE_OPENAL_DLOPEN + endif + endif + + ifeq ($(USE_CURL),1) + BASE_CFLAGS += -DUSE_CURL + ifeq ($(USE_CURL_DLOPEN),1) + BASE_CFLAGS += -DUSE_CURL_DLOPEN + endif + endif + + ifeq ($(USE_CODEC_VORBIS),1) + BASE_CFLAGS += -DUSE_CODEC_VORBIS + endif + + OPTIMIZE = -O3 -ffast-math -funroll-loops -fomit-frame-pointer + + ifeq ($(ARCH),x86_64) + OPTIMIZE = -O3 -fomit-frame-pointer -ffast-math -funroll-loops \ + -falign-loops=2 -falign-jumps=2 -falign-functions=2 \ + -fstrength-reduce + # experimental x86_64 jit compiler! you need GNU as + HAVE_VM_COMPILED = true + else + ifeq ($(ARCH),i386) + OPTIMIZE = -O3 -march=i586 -fomit-frame-pointer -ffast-math \ + -funroll-loops -falign-loops=2 -falign-jumps=2 \ + -falign-functions=2 -fstrength-reduce + HAVE_VM_COMPILED=true + else + ifeq ($(ARCH),ppc) + BASE_CFLAGS += -maltivec + HAVE_VM_COMPILED=true + endif + ifeq ($(ARCH),ppc64) + BASE_CFLAGS += -maltivec + HAVE_VM_COMPILED=true + endif + ifeq ($(ARCH),sparc) + OPTIMIZE += -mtune=ultrasparc3 -mv8plus + HAVE_VM_COMPILED=true + endif + endif + endif + + ifneq ($(HAVE_VM_COMPILED),true) + BASE_CFLAGS += -DNO_VM_COMPILED + endif + + SHLIBEXT=so + SHLIBCFLAGS=-fPIC + SHLIBLDFLAGS=-shared $(LDFLAGS) + + THREAD_LIBS=-lpthread + LIBS=-ldl -lm + + CLIENT_LIBS=$(SDL_LIBS) -lGL + + ifeq ($(USE_OPENAL),1) + ifneq ($(USE_OPENAL_DLOPEN),1) + CLIENT_LIBS += -lopenal + endif + endif + + ifeq ($(USE_CURL),1) + ifneq ($(USE_CURL_DLOPEN),1) + CLIENT_LIBS += -lcurl + endif + endif + + ifeq ($(USE_CODEC_VORBIS),1) + CLIENT_LIBS += -lvorbisfile -lvorbis -logg + endif + + ifeq ($(USE_MUMBLE),1) + CLIENT_LIBS += -lrt + endif + +ifeq ($(USE_LOCAL_HEADERS),1) + BASE_CFLAGS += -I$(SDLHDIR)/include + endif + + ifeq ($(ARCH),i386) + # linux32 make ... + BASE_CFLAGS += -m32 + else + ifeq ($(ARCH),ppc64) + BASE_CFLAGS += -m64 + endif + endif + + DEBUG_CFLAGS = $(BASE_CFLAGS) -g -O0 + RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG $(OPTIMIZE) + +else # ifeq Linux + +############################################################################# +# SETUP AND BUILD -- MAC OS X +############################################################################# + +ifeq ($(PLATFORM),darwin) + HAVE_VM_COMPILED=true + CLIENT_LIBS= + OPTIMIZE= + + BASE_CFLAGS = -Wall -Wimplicit -Wstrict-prototypes + + ifeq ($(ARCH),ppc) + BASE_CFLAGS += -faltivec + OPTIMIZE += -O3 + endif + ifeq ($(ARCH),ppc64) + BASE_CFLAGS += -faltivec + endif + ifeq ($(ARCH),i386) + OPTIMIZE += -march=prescott -mfpmath=sse + # x86 vm will crash without -mstackrealign since MMX instructions will be + # used no matter what and they corrupt the frame pointer in VM calls + BASE_CFLAGS += -mstackrealign + endif + + BASE_CFLAGS += -fno-strict-aliasing -DMACOS_X -fno-common -pipe + + ifeq ($(USE_OPENAL),1) + BASE_CFLAGS += -DUSE_OPENAL + ifneq ($(USE_OPENAL_DLOPEN),1) + CLIENT_LIBS += -framework OpenAL + else + BASE_CFLAGS += -DUSE_OPENAL_DLOPEN + endif + endif + + ifeq ($(USE_CURL),1) + BASE_CFLAGS += -DUSE_CURL + ifneq ($(USE_CURL_DLOPEN),1) + CLIENT_LIBS += -lcurl + else + BASE_CFLAGS += -DUSE_CURL_DLOPEN + endif + endif + + ifeq ($(USE_CODEC_VORBIS),1) + BASE_CFLAGS += -DUSE_CODEC_VORBIS + CLIENT_LIBS += -lvorbisfile -lvorbis -logg + endif + + BASE_CFLAGS += -D_THREAD_SAFE=1 + + ifeq ($(USE_LOCAL_HEADERS),1) + BASE_CFLAGS += -I$(SDLHDIR)/include + endif + + # We copy sdlmain before ranlib'ing it so that subversion doesn't think + # the file has been modified by each build. + LIBSDLMAIN=$(B)/libSDLmain.a + LIBSDLMAINSRC=$(LIBSDIR)/macosx/libSDLmain.a + CLIENT_LIBS += -framework Cocoa -framework IOKit -framework OpenGL \ + $(LIBSDIR)/macosx/libSDL-1.2.0.dylib + + OPTIMIZE += -ffast-math -falign-loops=16 + + ifneq ($(HAVE_VM_COMPILED),true) + BASE_CFLAGS += -DNO_VM_COMPILED + endif + + DEBUG_CFLAGS = $(BASE_CFLAGS) -g -O0 + + RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG $(OPTIMIZE) + + SHLIBEXT=dylib + SHLIBCFLAGS=-fPIC -fno-common + SHLIBLDFLAGS=-dynamiclib $(LDFLAGS) + + NOTSHLIBCFLAGS=-mdynamic-no-pic + + TOOLS_CFLAGS += -DMACOS_X + +else # ifeq darwin + + +############################################################################# +# SETUP AND BUILD -- MINGW32 +############################################################################# + +ifeq ($(PLATFORM),mingw32) + + ifndef WINDRES + WINDRES=windres + endif + + ARCH=x86 + + BASE_CFLAGS = -Wall -fno-strict-aliasing -Wimplicit -Wstrict-prototypes \ + -DUSE_ICON + + # In the absence of wspiapi.h, require Windows XP or later + ifeq ($(shell test -e $(CMDIR)/wspiapi.h; echo $$?),1) + BASE_CFLAGS += -DWINVER=0x501 + endif + + ifeq ($(USE_OPENAL),1) + BASE_CFLAGS += -DUSE_OPENAL + BASE_CFLAGS += $(OPENAL_CFLAGS) + ifeq ($(USE_OPENAL_DLOPEN),1) + BASE_CFLAGS += -DUSE_OPENAL_DLOPEN + else + CLIENT_LDFLAGS += $(OPENAL_LDFLAGS) + endif + endif + + ifeq ($(USE_CODEC_VORBIS),1) + BASE_CFLAGS += -DUSE_CODEC_VORBIS + endif + + OPTIMIZE = -O3 -march=i586 -fno-omit-frame-pointer -ffast-math \ + -falign-loops=2 -funroll-loops -falign-jumps=2 -falign-functions=2 \ + -fstrength-reduce + + HAVE_VM_COMPILED = true + + SHLIBEXT=dll + SHLIBCFLAGS= + SHLIBLDFLAGS=-shared $(LDFLAGS) + + BINEXT=.exe + + LIBS= -lws2_32 -lwinmm + CLIENT_LDFLAGS = -mwindows + CLIENT_LIBS = -lgdi32 -lole32 -lopengl32 + + ifeq ($(USE_CURL),1) + BASE_CFLAGS += -DUSE_CURL + BASE_CFLAGS += $(CURL_CFLAGS) + ifneq ($(USE_CURL_DLOPEN),1) + ifeq ($(USE_LOCAL_HEADERS),1) + BASE_CFLAGS += -DCURL_STATICLIB + CLIENT_LIBS += $(LIBSDIR)/win32/libcurl.a + else + CLIENT_LIBS += $(CURL_LIBS) + endif + endif + endif + + ifeq ($(USE_CODEC_VORBIS),1) + CLIENT_LIBS += -lvorbisfile -lvorbis -logg + endif + + ifeq ($(ARCH),x86) + # build 32bit + BASE_CFLAGS += -m32 + endif + + DEBUG_CFLAGS=$(BASE_CFLAGS) -g -O0 + RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG $(OPTIMIZE) + + # libmingw32 must be linked before libSDLmain + CLIENT_LIBS += -lmingw32 + ifeq ($(USE_LOCAL_HEADERS),1) + BASE_CFLAGS += -I$(SDLHDIR)/include + CLIENT_LIBS += $(LIBSDIR)/win32/libSDLmain.a \ + $(LIBSDIR)/win32/libSDL.dll.a + else + BASE_CFLAGS += $(SDL_CFLAGS) + CLIENT_LIBS += $(SDL_LIBS) + endif + + + + BUILD_CLIENT_SMP = 0 + +else # ifeq mingw32 + +############################################################################# +# SETUP AND BUILD -- FREEBSD +############################################################################# + +ifeq ($(PLATFORM),freebsd) + + ifneq (,$(findstring alpha,$(shell uname -m))) + ARCH=axp + else #default to i386 + ARCH=i386 + endif #alpha test + + + BASE_CFLAGS = -Wall -fno-strict-aliasing -Wimplicit -Wstrict-prototypes \ + -DUSE_ICON $(SDL_CFLAGS) + + ifeq ($(USE_OPENAL),1) + BASE_CFLAGS += -DUSE_OPENAL + ifeq ($(USE_OPENAL_DLOPEN),1) + BASE_CFLAGS += -DUSE_OPENAL_DLOPEN + endif + endif + + ifeq ($(USE_CODEC_VORBIS),1) + BASE_CFLAGS += -DUSE_CODEC_VORBIS + endif + + ifeq ($(ARCH),axp) + BASE_CFLAGS += -DNO_VM_COMPILED + RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O3 -ffast-math -funroll-loops \ + -fomit-frame-pointer -fexpensive-optimizations + else + ifeq ($(ARCH),i386) + RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O3 -mtune=pentiumpro \ + -march=pentium -fomit-frame-pointer -pipe -ffast-math \ + -falign-loops=2 -falign-jumps=2 -falign-functions=2 \ + -funroll-loops -fstrength-reduce + HAVE_VM_COMPILED=true + else + BASE_CFLAGS += -DNO_VM_COMPILED + endif + endif + + DEBUG_CFLAGS=$(BASE_CFLAGS) -g + + SHLIBEXT=so + SHLIBCFLAGS=-fPIC + SHLIBLDFLAGS=-shared $(LDFLAGS) + + THREAD_LIBS=-lpthread + # don't need -ldl (FreeBSD) + LIBS=-lm + + CLIENT_LIBS = + + CLIENT_LIBS += $(SDL_LIBS) -lGL + + ifeq ($(USE_OPENAL),1) + ifneq ($(USE_OPENAL_DLOPEN),1) + CLIENT_LIBS += $(THREAD_LIBS) -lopenal + endif + endif + + ifeq ($(USE_CODEC_VORBIS),1) + CLIENT_LIBS += -lvorbisfile -lvorbis -logg + endif + +else # ifeq freebsd + +############################################################################# +# SETUP AND BUILD -- OPENBSD +############################################################################# + +ifeq ($(PLATFORM),openbsd) + + #default to i386, no tests done on anything else + ARCH=i386 + + + BASE_CFLAGS = -Wall -fno-strict-aliasing -Wimplicit -Wstrict-prototypes \ + -DUSE_ICON $(SDL_CFLAGS) + + ifeq ($(USE_OPENAL),1) + BASE_CFLAGS += -DUSE_OPENAL + ifeq ($(USE_OPENAL_DLOPEN),1) + BASE_CFLAGS += -DUSE_OPENAL_DLOPEN + endif + endif + + ifeq ($(USE_CODEC_VORBIS),1) + BASE_CFLAGS += -DUSE_CODEC_VORBIS + endif + + BASE_CFLAGS += -DNO_VM_COMPILED -I/usr/X11R6/include -I/usr/local/include + RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O3 \ + -march=pentium -fomit-frame-pointer -pipe -ffast-math \ + -falign-loops=2 -falign-jumps=2 -falign-functions=2 \ + -funroll-loops -fstrength-reduce + HAVE_VM_COMPILED=false + + DEBUG_CFLAGS=$(BASE_CFLAGS) -g + + SHLIBEXT=so + SHLIBCFLAGS=-fPIC + SHLIBLDFLAGS=-shared $(LDFLAGS) + + THREAD_LIBS=-lpthread + LIBS=-lm + + CLIENT_LIBS = + + CLIENT_LIBS += $(SDL_LIBS) -lGL + + ifeq ($(USE_OPENAL),1) + ifneq ($(USE_OPENAL_DLOPEN),1) + CLIENT_LIBS += $(THREAD_LIBS) -lossaudio -lopenal + endif + endif + + ifeq ($(USE_CODEC_VORBIS),1) + CLIENT_LIBS += -lvorbisfile -lvorbis -logg + endif + +else # ifeq openbsd + +############################################################################# +# SETUP AND BUILD -- NETBSD +############################################################################# + +ifeq ($(PLATFORM),netbsd) + + ifeq ($(shell uname -m),i386) + ARCH=i386 + endif + + LIBS=-lm + SHLIBEXT=so + SHLIBCFLAGS=-fPIC + SHLIBLDFLAGS=-shared $(LDFLAGS) + THREAD_LIBS=-lpthread + + BASE_CFLAGS = -Wall -fno-strict-aliasing -Wimplicit -Wstrict-prototypes + + ifneq ($(ARCH),i386) + BASE_CFLAGS += -DNO_VM_COMPILED + endif + + DEBUG_CFLAGS=$(BASE_CFLAGS) -g + + BUILD_CLIENT = 0 + BUILD_GAME_QVM = 0 + +else # ifeq netbsd + +############################################################################# +# SETUP AND BUILD -- IRIX +############################################################################# + +ifeq ($(PLATFORM),irix64) + + ARCH=mips #default to MIPS + + CC = c99 + MKDIR = mkdir -p + + BASE_CFLAGS=-Dstricmp=strcasecmp -Xcpluscomm -woff 1185 \ + -I. $(SDL_CFLAGS) -I$(ROOT)/usr/include -DNO_VM_COMPILED + RELEASE_CFLAGS=$(BASE_CFLAGS) -O3 + DEBUG_CFLAGS=$(BASE_CFLAGS) -g + + SHLIBEXT=so + SHLIBCFLAGS= + SHLIBLDFLAGS=-shared + + LIBS=-ldl -lm -lgen + # FIXME: The X libraries probably aren't necessary? + CLIENT_LIBS=-L/usr/X11/$(LIB) $(SDL_LIBS) -lGL \ + -lX11 -lXext -lm + +else # ifeq IRIX + +############################################################################# +# SETUP AND BUILD -- SunOS +############################################################################# + +ifeq ($(PLATFORM),sunos) + + CC=gcc + INSTALL=ginstall + MKDIR=gmkdir + COPYDIR="/usr/local/share/games/quake3" + + ifneq (,$(findstring i86pc,$(shell uname -m))) + ARCH=i386 + else #default to sparc + ARCH=sparc + endif + + ifneq ($(ARCH),i386) + ifneq ($(ARCH),sparc) + $(error arch $(ARCH) is currently not supported) + endif + endif + + BASE_CFLAGS = -Wall -fno-strict-aliasing -Wimplicit -Wstrict-prototypes \ + -pipe -DUSE_ICON $(SDL_CFLAGS) + + OPTIMIZE = -O3 -ffast-math -funroll-loops + + ifeq ($(ARCH),sparc) + OPTIMIZE = -O3 -ffast-math \ + -fstrength-reduce -falign-functions=2 \ + -mtune=ultrasparc3 -mv8plus -mno-faster-structs \ + -funroll-loops #-mv8plus + HAVE_VM_COMPILED=true + else + ifeq ($(ARCH),i386) + OPTIMIZE = -O3 -march=i586 -fomit-frame-pointer -ffast-math \ + -funroll-loops -falign-loops=2 -falign-jumps=2 \ + -falign-functions=2 -fstrength-reduce + HAVE_VM_COMPILED=true + BASE_CFLAGS += -m32 + BASE_CFLAGS += -I/usr/X11/include/NVIDIA + CLIENT_LDFLAGS += -L/usr/X11/lib/NVIDIA -R/usr/X11/lib/NVIDIA + endif + endif + + ifneq ($(HAVE_VM_COMPILED),true) + BASE_CFLAGS += -DNO_VM_COMPILED + endif + + DEBUG_CFLAGS = $(BASE_CFLAGS) -ggdb -O0 + + RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG $(OPTIMIZE) + + SHLIBEXT=so + SHLIBCFLAGS=-fPIC + SHLIBLDFLAGS=-shared $(LDFLAGS) + + THREAD_LIBS=-lpthread + LIBS=-lsocket -lnsl -ldl -lm + + BOTCFLAGS=-O0 + + CLIENT_LIBS +=$(SDL_LIBS) -lGL -lX11 -lXext -liconv -lm + +else # ifeq sunos + +############################################################################# +# SETUP AND BUILD -- GENERIC +############################################################################# + BASE_CFLAGS=-DNO_VM_COMPILED + DEBUG_CFLAGS=$(BASE_CFLAGS) -g + RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O3 + + SHLIBEXT=so + SHLIBCFLAGS=-fPIC + SHLIBLDFLAGS=-shared + +endif #Linux +endif #darwin +endif #mingw32 +endif #FreeBSD +endif #OpenBSD +endif #NetBSD +endif #IRIX +endif #SunOS + +TARGETS = + +ifneq ($(BUILD_SERVER),0) + TARGETS += $(B)/Reactionded.$(ARCH)$(BINEXT) +endif + +ifneq ($(BUILD_CLIENT),0) + TARGETS += $(B)/Reaction.$(ARCH)$(BINEXT) + ifneq ($(BUILD_CLIENT_SMP),0) + TARGETS += $(B)/Reaction-smp.$(ARCH)$(BINEXT) + endif +endif + +ifneq ($(BUILD_GAME_SO),0) + TARGETS += \ + $(B)/baseq3/cgame$(ARCH).$(SHLIBEXT) \ + $(B)/baseq3/qagame$(ARCH).$(SHLIBEXT) \ + $(B)/baseq3/ui$(ARCH).$(SHLIBEXT) + ifneq ($(BUILD_MISSIONPACK),0) + TARGETS += \ + $(B)/missionpack/cgame$(ARCH).$(SHLIBEXT) \ + $(B)/missionpack/qagame$(ARCH).$(SHLIBEXT) \ + $(B)/missionpack/ui$(ARCH).$(SHLIBEXT) + endif +endif + +ifneq ($(BUILD_GAME_QVM),0) + ifneq ($(CROSS_COMPILING),1) + TARGETS += \ + $(B)/baseq3/vm/cgame.qvm \ + $(B)/baseq3/vm/qagame.qvm \ + $(B)/baseq3/vm/ui.qvm + ifneq ($(BUILD_MISSIONPACK),0) + TARGETS += \ + $(B)/missionpack/vm/qagame.qvm \ + $(B)/missionpack/vm/cgame.qvm \ + $(B)/missionpack/vm/ui.qvm + endif + endif +endif + +ifeq ($(USE_MUMBLE),1) + BASE_CFLAGS += -DUSE_MUMBLE +endif + +ifeq ($(USE_VOIP),1) + BASE_CFLAGS += -DUSE_VOIP + ifeq ($(USE_INTERNAL_SPEEX),1) + BASE_CFLAGS += -DFLOATING_POINT -DUSE_ALLOCA -I$(SPEEXDIR)/include + else + CLIENT_LIBS += -lspeex + endif +endif + +ifdef DEFAULT_BASEDIR + BASE_CFLAGS += -DDEFAULT_BASEDIR=\\\"$(DEFAULT_BASEDIR)\\\" +endif + +ifeq ($(USE_LOCAL_HEADERS),1) + BASE_CFLAGS += -DUSE_LOCAL_HEADERS +endif + +ifeq ($(BUILD_STANDALONE),1) + BASE_CFLAGS += -DSTANDALONE +endif + +ifeq ($(GENERATE_DEPENDENCIES),1) + DEPEND_CFLAGS = -MMD +else + DEPEND_CFLAGS = +endif + +BASE_CFLAGS += -DPRODUCT_VERSION=\\\"$(VERSION)\\\" + +ifeq ($(V),1) +echo_cmd=@: +Q= +else +echo_cmd=@echo +Q=@ +endif + +define DO_CC +$(echo_cmd) "CC $<" +$(Q)$(CC) $(NOTSHLIBCFLAGS) $(CFLAGS) -o $@ -c $< +endef + +define DO_SMP_CC +$(echo_cmd) "SMP_CC $<" +$(Q)$(CC) $(NOTSHLIBCFLAGS) $(CFLAGS) -DSMP -o $@ -c $< +endef + +define DO_BOT_CC +$(echo_cmd) "BOT_CC $<" +$(Q)$(CC) $(NOTSHLIBCFLAGS) $(CFLAGS) $(BOTCFLAGS) -DBOTLIB -o $@ -c $< +endef + +ifeq ($(GENERATE_DEPENDENCIES),1) + DO_QVM_DEP=cat $(@:%.o=%.d) | sed -e 's/\.o/\.asm/g' >> $(@:%.o=%.d) +endif + +define DO_SHLIB_CC +$(echo_cmd) "SHLIB_CC $<" +$(Q)$(CC) $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< +$(Q)$(DO_QVM_DEP) +endef + +define DO_GAME_CC +$(echo_cmd) "GAME_CC $<" +$(Q)$(CC) -DQAGAME $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< +$(Q)$(DO_QVM_DEP) +endef + +define DO_CGAME_CC +$(echo_cmd) "CGAME_CC $<" +$(Q)$(CC) -DCGAME $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< +$(Q)$(DO_QVM_DEP) +endef + +define DO_UI_CC +$(echo_cmd) "UI_CC $<" +$(Q)$(CC) -DUI $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< +$(Q)$(DO_QVM_DEP) +endef + +define DO_SHLIB_CC_MISSIONPACK +$(echo_cmd) "SHLIB_CC_MISSIONPACK $<" +$(Q)$(CC) -DMISSIONPACK $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< +$(Q)$(DO_QVM_DEP) +endef + +define DO_GAME_CC_MISSIONPACK +$(echo_cmd) "GAME_CC_MISSIONPACK $<" +$(Q)$(CC) -DMISSIONPACK -DQAGAME $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< +$(Q)$(DO_QVM_DEP) +endef + +define DO_CGAME_CC_MISSIONPACK +$(echo_cmd) "CGAME_CC_MISSIONPACK $<" +$(Q)$(CC) -DMISSIONPACK -DCGAME $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< +$(Q)$(DO_QVM_DEP) +endef + +define DO_UI_CC_MISSIONPACK +$(echo_cmd) "UI_CC_MISSIONPACK $<" +$(Q)$(CC) -DMISSIONPACK -DUI $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< +$(Q)$(DO_QVM_DEP) +endef + +define DO_AS +$(echo_cmd) "AS $<" +$(Q)$(CC) $(CFLAGS) -x assembler-with-cpp -o $@ -c $< +endef + +define DO_DED_CC +$(echo_cmd) "DED_CC $<" +$(Q)$(CC) $(NOTSHLIBCFLAGS) -DDEDICATED $(CFLAGS) -o $@ -c $< +endef + +define DO_WINDRES +$(echo_cmd) "WINDRES $<" +$(Q)$(WINDRES) -i $< -o $@ +endef + + +############################################################################# +# MAIN TARGETS +############################################################################# + +default: release +all: debug release + +debug: + @$(MAKE) targets B=$(BD) CFLAGS="$(CFLAGS) $(DEPEND_CFLAGS) \ + $(DEBUG_CFLAGS)" V=$(V) + +release: + @$(MAKE) targets B=$(BR) CFLAGS="$(CFLAGS) $(DEPEND_CFLAGS) \ + $(RELEASE_CFLAGS)" V=$(V) + +# Create the build directories, check libraries and print out +# an informational message, then start building +targets: makedirs + @echo "" + @echo "Building Reaction in $(B):" + @echo " PLATFORM: $(PLATFORM)" + @echo " ARCH: $(ARCH)" + @echo " VERSION: $(VERSION)" + @echo " COMPILE_PLATFORM: $(COMPILE_PLATFORM)" + @echo " COMPILE_ARCH: $(COMPILE_ARCH)" + @echo " CC: $(CC)" + @echo "" + @echo " CFLAGS:" + -@for i in $(CFLAGS); \ + do \ + echo " $$i"; \ + done + @echo "" + @echo " LDFLAGS:" + -@for i in $(LDFLAGS); \ + do \ + echo " $$i"; \ + done + @echo "" + @echo " LIBS:" + -@for i in $(LIBS); \ + do \ + echo " $$i"; \ + done + @echo "" + @echo " CLIENT_LIBS:" + -@for i in $(CLIENT_LIBS); \ + do \ + echo " $$i"; \ + done + @echo "" + @echo " Output:" + -@for i in $(TARGETS); \ + do \ + echo " $$i"; \ + done + @echo "" +ifneq ($(TARGETS),) + @$(MAKE) $(TARGETS) V=$(V) +endif + +makedirs: + @if [ ! -d $(BUILD_DIR) ];then $(MKDIR) $(BUILD_DIR);fi + @if [ ! -d $(B) ];then $(MKDIR) $(B);fi + @if [ ! -d $(B)/client ];then $(MKDIR) $(B)/client;fi + @if [ ! -d $(B)/clientsmp ];then $(MKDIR) $(B)/clientsmp;fi + @if [ ! -d $(B)/ded ];then $(MKDIR) $(B)/ded;fi + @if [ ! -d $(B)/baseq3 ];then $(MKDIR) $(B)/baseq3;fi + @if [ ! -d $(B)/baseq3/cgame ];then $(MKDIR) $(B)/baseq3/cgame;fi + @if [ ! -d $(B)/baseq3/game ];then $(MKDIR) $(B)/baseq3/game;fi + @if [ ! -d $(B)/baseq3/ui ];then $(MKDIR) $(B)/baseq3/ui;fi + @if [ ! -d $(B)/baseq3/qcommon ];then $(MKDIR) $(B)/baseq3/qcommon;fi + @if [ ! -d $(B)/baseq3/vm ];then $(MKDIR) $(B)/baseq3/vm;fi + @if [ ! -d $(B)/missionpack ];then $(MKDIR) $(B)/missionpack;fi + @if [ ! -d $(B)/missionpack/cgame ];then $(MKDIR) $(B)/missionpack/cgame;fi + @if [ ! -d $(B)/missionpack/game ];then $(MKDIR) $(B)/missionpack/game;fi + @if [ ! -d $(B)/missionpack/ui ];then $(MKDIR) $(B)/missionpack/ui;fi + @if [ ! -d $(B)/missionpack/qcommon ];then $(MKDIR) $(B)/missionpack/qcommon;fi + @if [ ! -d $(B)/missionpack/vm ];then $(MKDIR) $(B)/missionpack/vm;fi + @if [ ! -d $(B)/tools ];then $(MKDIR) $(B)/tools;fi + @if [ ! -d $(B)/tools/asm ];then $(MKDIR) $(B)/tools/asm;fi + @if [ ! -d $(B)/tools/etc ];then $(MKDIR) $(B)/tools/etc;fi + @if [ ! -d $(B)/tools/rcc ];then $(MKDIR) $(B)/tools/rcc;fi + @if [ ! -d $(B)/tools/cpp ];then $(MKDIR) $(B)/tools/cpp;fi + @if [ ! -d $(B)/tools/lburg ];then $(MKDIR) $(B)/tools/lburg;fi + +############################################################################# +# QVM BUILD TOOLS +############################################################################# + +TOOLS_OPTIMIZE = -g -O2 -Wall -fno-strict-aliasing +TOOLS_CFLAGS = $(TOOLS_OPTIMIZE) \ + -DTEMPDIR=\"$(TEMPDIR)\" -DSYSTEM=\"\" \ + -I$(Q3LCCSRCDIR) \ + -I$(LBURGDIR) +TOOLS_LIBS = +TOOLS_LDFLAGS = + +ifeq ($(GENERATE_DEPENDENCIES),1) + TOOLS_CFLAGS += -MMD +endif + +define DO_TOOLS_CC +$(echo_cmd) "TOOLS_CC $<" +$(Q)$(CC) $(TOOLS_CFLAGS) -o $@ -c $< +endef + +define DO_TOOLS_CC_DAGCHECK +$(echo_cmd) "TOOLS_CC_DAGCHECK $<" +$(Q)$(CC) $(TOOLS_CFLAGS) -Wno-unused -o $@ -c $< +endef + +LBURG = $(B)/tools/lburg/lburg$(BINEXT) +DAGCHECK_C = $(B)/tools/rcc/dagcheck.c +Q3RCC = $(B)/tools/q3rcc$(BINEXT) +Q3CPP = $(B)/tools/q3cpp$(BINEXT) +Q3LCC = $(B)/tools/q3lcc$(BINEXT) +Q3ASM = $(B)/tools/q3asm$(BINEXT) + +LBURGOBJ= \ + $(B)/tools/lburg/lburg.o \ + $(B)/tools/lburg/gram.o + +$(B)/tools/lburg/%.o: $(LBURGDIR)/%.c + $(DO_TOOLS_CC) + +$(LBURG): $(LBURGOBJ) + $(echo_cmd) "LD $@" + $(Q)$(CC) $(TOOLS_CFLAGS) $(TOOLS_LDFLAGS) -o $@ $^ $(TOOLS_LIBS) + +Q3RCCOBJ = \ + $(B)/tools/rcc/alloc.o \ + $(B)/tools/rcc/bind.o \ + $(B)/tools/rcc/bytecode.o \ + $(B)/tools/rcc/dag.o \ + $(B)/tools/rcc/dagcheck.o \ + $(B)/tools/rcc/decl.o \ + $(B)/tools/rcc/enode.o \ + $(B)/tools/rcc/error.o \ + $(B)/tools/rcc/event.o \ + $(B)/tools/rcc/expr.o \ + $(B)/tools/rcc/gen.o \ + $(B)/tools/rcc/init.o \ + $(B)/tools/rcc/inits.o \ + $(B)/tools/rcc/input.o \ + $(B)/tools/rcc/lex.o \ + $(B)/tools/rcc/list.o \ + $(B)/tools/rcc/main.o \ + $(B)/tools/rcc/null.o \ + $(B)/tools/rcc/output.o \ + $(B)/tools/rcc/prof.o \ + $(B)/tools/rcc/profio.o \ + $(B)/tools/rcc/simp.o \ + $(B)/tools/rcc/stmt.o \ + $(B)/tools/rcc/string.o \ + $(B)/tools/rcc/sym.o \ + $(B)/tools/rcc/symbolic.o \ + $(B)/tools/rcc/trace.o \ + $(B)/tools/rcc/tree.o \ + $(B)/tools/rcc/types.o + +$(DAGCHECK_C): $(LBURG) $(Q3LCCSRCDIR)/dagcheck.md + $(echo_cmd) "LBURG $(Q3LCCSRCDIR)/dagcheck.md" + $(Q)$(LBURG) $(Q3LCCSRCDIR)/dagcheck.md $@ + +$(B)/tools/rcc/dagcheck.o: $(DAGCHECK_C) + $(DO_TOOLS_CC_DAGCHECK) + +$(B)/tools/rcc/%.o: $(Q3LCCSRCDIR)/%.c + $(DO_TOOLS_CC) + +$(Q3RCC): $(Q3RCCOBJ) + $(echo_cmd) "LD $@" + $(Q)$(CC) $(TOOLS_CFLAGS) $(TOOLS_LDFLAGS) -o $@ $^ $(TOOLS_LIBS) + +Q3CPPOBJ = \ + $(B)/tools/cpp/cpp.o \ + $(B)/tools/cpp/lex.o \ + $(B)/tools/cpp/nlist.o \ + $(B)/tools/cpp/tokens.o \ + $(B)/tools/cpp/macro.o \ + $(B)/tools/cpp/eval.o \ + $(B)/tools/cpp/include.o \ + $(B)/tools/cpp/hideset.o \ + $(B)/tools/cpp/getopt.o \ + $(B)/tools/cpp/unix.o + +$(B)/tools/cpp/%.o: $(Q3CPPDIR)/%.c + $(DO_TOOLS_CC) + +$(Q3CPP): $(Q3CPPOBJ) + $(echo_cmd) "LD $@" + $(Q)$(CC) $(TOOLS_CFLAGS) $(TOOLS_LDFLAGS) -o $@ $^ $(TOOLS_LIBS) + +Q3LCCOBJ = \ + $(B)/tools/etc/lcc.o \ + $(B)/tools/etc/bytecode.o + +$(B)/tools/etc/%.o: $(Q3LCCETCDIR)/%.c + $(DO_TOOLS_CC) + +$(Q3LCC): $(Q3LCCOBJ) $(Q3RCC) $(Q3CPP) + $(echo_cmd) "LD $@" + $(Q)$(CC) $(TOOLS_CFLAGS) $(TOOLS_LDFLAGS) -o $@ $(Q3LCCOBJ) $(TOOLS_LIBS) + +define DO_Q3LCC +$(echo_cmd) "Q3LCC $<" +$(Q)$(Q3LCC) -o $@ $< +endef + +define DO_CGAME_Q3LCC +$(echo_cmd) "CGAME_Q3LCC $<" +$(Q)$(Q3LCC) -DCGAME -o $@ $< +endef + +define DO_GAME_Q3LCC +$(echo_cmd) "GAME_Q3LCC $<" +$(Q)$(Q3LCC) -DQAGAME -o $@ $< +endef + +define DO_UI_Q3LCC +$(echo_cmd) "UI_Q3LCC $<" +$(Q)$(Q3LCC) -DUI -o $@ $< +endef + +define DO_Q3LCC_MISSIONPACK +$(echo_cmd) "Q3LCC_MISSIONPACK $<" +$(Q)$(Q3LCC) -DMISSIONPACK -o $@ $< +endef + +define DO_CGAME_Q3LCC_MISSIONPACK +$(echo_cmd) "CGAME_Q3LCC_MISSIONPACK $<" +$(Q)$(Q3LCC) -DMISSIONPACK -DCGAME -o $@ $< +endef + +define DO_GAME_Q3LCC_MISSIONPACK +$(echo_cmd) "GAME_Q3LCC_MISSIONPACK $<" +$(Q)$(Q3LCC) -DMISSIONPACK -DQAGAME -o $@ $< +endef + +define DO_UI_Q3LCC_MISSIONPACK +$(echo_cmd) "UI_Q3LCC_MISSIONPACK $<" +$(Q)$(Q3LCC) -DMISSIONPACK -DUI -o $@ $< +endef + + +Q3ASMOBJ = \ + $(B)/tools/asm/q3asm.o \ + $(B)/tools/asm/cmdlib.o + +$(B)/tools/asm/%.o: $(Q3ASMDIR)/%.c + $(DO_TOOLS_CC) + +$(Q3ASM): $(Q3ASMOBJ) + $(echo_cmd) "LD $@" + $(Q)$(CC) $(TOOLS_CFLAGS) $(TOOLS_LDFLAGS) -o $@ $^ $(TOOLS_LIBS) + + +############################################################################# +# CLIENT/SERVER +############################################################################# + +Q3OBJ = \ + $(B)/client/cl_cgame.o \ + $(B)/client/cl_cin.o \ + $(B)/client/cl_console.o \ + $(B)/client/cl_input.o \ + $(B)/client/cl_keys.o \ + $(B)/client/cl_main.o \ + $(B)/client/cl_net_chan.o \ + $(B)/client/cl_parse.o \ + $(B)/client/cl_scrn.o \ + $(B)/client/cl_ui.o \ + $(B)/client/cl_avi.o \ + \ + $(B)/client/cm_load.o \ + $(B)/client/cm_patch.o \ + $(B)/client/cm_polylib.o \ + $(B)/client/cm_test.o \ + $(B)/client/cm_trace.o \ + \ + $(B)/client/cmd.o \ + $(B)/client/common.o \ + $(B)/client/cvar.o \ + $(B)/client/files.o \ + $(B)/client/md4.o \ + $(B)/client/md5.o \ + $(B)/client/msg.o \ + $(B)/client/net_chan.o \ + $(B)/client/net_ip.o \ + $(B)/client/huffman.o \ + \ + $(B)/client/snd_adpcm.o \ + $(B)/client/snd_dma.o \ + $(B)/client/snd_mem.o \ + $(B)/client/snd_mix.o \ + $(B)/client/snd_wavelet.o \ + \ + $(B)/client/snd_main.o \ + $(B)/client/snd_codec.o \ + $(B)/client/snd_codec_wav.o \ + $(B)/client/snd_codec_ogg.o \ + \ + $(B)/client/qal.o \ + $(B)/client/snd_openal.o \ + \ + $(B)/client/cl_curl.o \ + \ + $(B)/client/sv_bot.o \ + $(B)/client/sv_ccmds.o \ + $(B)/client/sv_client.o \ + $(B)/client/sv_game.o \ + $(B)/client/sv_init.o \ + $(B)/client/sv_main.o \ + $(B)/client/sv_net_chan.o \ + $(B)/client/sv_snapshot.o \ + $(B)/client/sv_world.o \ + \ + $(B)/client/q_math.o \ + $(B)/client/q_shared.o \ + \ + $(B)/client/unzip.o \ + $(B)/client/puff.o \ + $(B)/client/vm.o \ + $(B)/client/vm_interpreted.o \ + \ + $(B)/client/be_aas_bspq3.o \ + $(B)/client/be_aas_cluster.o \ + $(B)/client/be_aas_debug.o \ + $(B)/client/be_aas_entity.o \ + $(B)/client/be_aas_file.o \ + $(B)/client/be_aas_main.o \ + $(B)/client/be_aas_move.o \ + $(B)/client/be_aas_optimize.o \ + $(B)/client/be_aas_reach.o \ + $(B)/client/be_aas_route.o \ + $(B)/client/be_aas_routealt.o \ + $(B)/client/be_aas_sample.o \ + $(B)/client/be_ai_char.o \ + $(B)/client/be_ai_chat.o \ + $(B)/client/be_ai_gen.o \ + $(B)/client/be_ai_goal.o \ + $(B)/client/be_ai_move.o \ + $(B)/client/be_ai_weap.o \ + $(B)/client/be_ai_weight.o \ + $(B)/client/be_ea.o \ + $(B)/client/be_interface.o \ + $(B)/client/l_crc.o \ + $(B)/client/l_libvar.o \ + $(B)/client/l_log.o \ + $(B)/client/l_memory.o \ + $(B)/client/l_precomp.o \ + $(B)/client/l_script.o \ + $(B)/client/l_struct.o \ + \ + $(B)/client/jcapimin.o \ + $(B)/client/jcapistd.o \ + $(B)/client/jccoefct.o \ + $(B)/client/jccolor.o \ + $(B)/client/jcdctmgr.o \ + $(B)/client/jchuff.o \ + $(B)/client/jcinit.o \ + $(B)/client/jcmainct.o \ + $(B)/client/jcmarker.o \ + $(B)/client/jcmaster.o \ + $(B)/client/jcomapi.o \ + $(B)/client/jcparam.o \ + $(B)/client/jcphuff.o \ + $(B)/client/jcprepct.o \ + $(B)/client/jcsample.o \ + $(B)/client/jdapimin.o \ + $(B)/client/jdapistd.o \ + $(B)/client/jdatasrc.o \ + $(B)/client/jdcoefct.o \ + $(B)/client/jdcolor.o \ + $(B)/client/jddctmgr.o \ + $(B)/client/jdhuff.o \ + $(B)/client/jdinput.o \ + $(B)/client/jdmainct.o \ + $(B)/client/jdmarker.o \ + $(B)/client/jdmaster.o \ + $(B)/client/jdpostct.o \ + $(B)/client/jdsample.o \ + $(B)/client/jdtrans.o \ + $(B)/client/jerror.o \ + $(B)/client/jfdctflt.o \ + $(B)/client/jidctflt.o \ + $(B)/client/jmemmgr.o \ + $(B)/client/jmemnobs.o \ + $(B)/client/jutils.o \ + \ + $(B)/client/tr_animation.o \ + $(B)/client/tr_backend.o \ + $(B)/client/tr_bsp.o \ + $(B)/client/tr_cmds.o \ + $(B)/client/tr_curve.o \ + $(B)/client/tr_flares.o \ + $(B)/client/tr_font.o \ + $(B)/client/tr_image.o \ + $(B)/client/tr_image_png.o \ + $(B)/client/tr_image_jpg.o \ + $(B)/client/tr_image_bmp.o \ + $(B)/client/tr_image_tga.o \ + $(B)/client/tr_image_pcx.o \ + $(B)/client/tr_init.o \ + $(B)/client/tr_light.o \ + $(B)/client/tr_main.o \ + $(B)/client/tr_marks.o \ + $(B)/client/tr_mesh.o \ + $(B)/client/tr_model.o \ + $(B)/client/tr_noise.o \ + $(B)/client/tr_scene.o \ + $(B)/client/tr_shade.o \ + $(B)/client/tr_shade_calc.o \ + $(B)/client/tr_shader.o \ + $(B)/client/tr_shadows.o \ + $(B)/client/tr_sky.o \ + $(B)/client/tr_surface.o \ + $(B)/client/tr_world.o \ + \ + $(B)/client/sdl_gamma.o \ + $(B)/client/sdl_input.o \ + $(B)/client/sdl_snd.o \ + \ + $(B)/client/con_passive.o \ + $(B)/client/con_log.o \ + $(B)/client/sys_main.o + +ifeq ($(ARCH),i386) + Q3OBJ += \ + $(B)/client/snd_mixa.o \ + $(B)/client/matha.o \ + $(B)/client/ftola.o \ + $(B)/client/snapvectora.o +endif +ifeq ($(ARCH),x86) + Q3OBJ += \ + $(B)/client/snd_mixa.o \ + $(B)/client/matha.o \ + $(B)/client/ftola.o \ + $(B)/client/snapvectora.o +endif + +ifeq ($(USE_VOIP),1) +ifeq ($(USE_INTERNAL_SPEEX),1) +Q3OBJ += \ + $(B)/client/bits.o \ + $(B)/client/buffer.o \ + $(B)/client/cb_search.o \ + $(B)/client/exc_10_16_table.o \ + $(B)/client/exc_10_32_table.o \ + $(B)/client/exc_20_32_table.o \ + $(B)/client/exc_5_256_table.o \ + $(B)/client/exc_5_64_table.o \ + $(B)/client/exc_8_128_table.o \ + $(B)/client/fftwrap.o \ + $(B)/client/filterbank.o \ + $(B)/client/filters.o \ + $(B)/client/gain_table.o \ + $(B)/client/gain_table_lbr.o \ + $(B)/client/hexc_10_32_table.o \ + $(B)/client/hexc_table.o \ + $(B)/client/high_lsp_tables.o \ + $(B)/client/jitter.o \ + $(B)/client/kiss_fft.o \ + $(B)/client/kiss_fftr.o \ + $(B)/client/lpc.o \ + $(B)/client/lsp.o \ + $(B)/client/lsp_tables_nb.o \ + $(B)/client/ltp.o \ + $(B)/client/mdf.o \ + $(B)/client/modes.o \ + $(B)/client/modes_wb.o \ + $(B)/client/nb_celp.o \ + $(B)/client/preprocess.o \ + $(B)/client/quant_lsp.o \ + $(B)/client/resample.o \ + $(B)/client/sb_celp.o \ + $(B)/client/smallft.o \ + $(B)/client/speex.o \ + $(B)/client/speex_callbacks.o \ + $(B)/client/speex_header.o \ + $(B)/client/stereo.o \ + $(B)/client/vbr.o \ + $(B)/client/vq.o \ + $(B)/client/window.o +endif +endif + + +ifeq ($(HAVE_VM_COMPILED),true) + ifeq ($(ARCH),i386) + Q3OBJ += $(B)/client/vm_x86.o + endif + ifeq ($(ARCH),x86) + Q3OBJ += $(B)/client/vm_x86.o + endif + ifeq ($(ARCH),x86_64) + Q3OBJ += $(B)/client/vm_x86_64.o $(B)/client/vm_x86_64_assembler.o + endif + ifeq ($(ARCH),ppc) + Q3OBJ += $(B)/client/vm_powerpc.o $(B)/client/vm_powerpc_asm.o + endif + ifeq ($(ARCH),ppc64) + Q3OBJ += $(B)/client/vm_powerpc.o $(B)/client/vm_powerpc_asm.o + endif + ifeq ($(ARCH),sparc) + Q3OBJ += $(B)/client/vm_sparc.o + endif +endif + +ifeq ($(PLATFORM),mingw32) + Q3OBJ += \ + $(B)/client/win_resource.o \ + $(B)/client/sys_win32.o +else + Q3OBJ += \ + $(B)/client/sys_unix.o +endif + +ifeq ($(USE_MUMBLE),1) + Q3OBJ += \ + $(B)/client/libmumblelink.o +endif + +Q3POBJ += \ + $(B)/client/sdl_glimp.o + +Q3POBJ_SMP += \ + $(B)/clientsmp/sdl_glimp.o + +$(B)/Reaction.$(ARCH)$(BINEXT): $(Q3OBJ) $(Q3POBJ) $(LIBSDLMAIN) + $(echo_cmd) "LD $@" + $(Q)$(CC) $(CLIENT_CFLAGS) $(CFLAGS) $(CLIENT_LDFLAGS) $(LDFLAGS) \ + -o $@ $(Q3OBJ) $(Q3POBJ) \ + $(LIBSDLMAIN) $(CLIENT_LIBS) $(LIBS) + +$(B)/Reaction-smp.$(ARCH)$(BINEXT): $(Q3OBJ) $(Q3POBJ_SMP) $(LIBSDLMAIN) + $(echo_cmd) "LD $@" + $(Q)$(CC) $(CLIENT_CFLAGS) $(CFLAGS) $(CLIENT_LDFLAGS) $(LDFLAGS) $(THREAD_LDFLAGS) \ + -o $@ $(Q3OBJ) $(Q3POBJ_SMP) \ + $(THREAD_LIBS) $(LIBSDLMAIN) $(CLIENT_LIBS) $(LIBS) + +ifneq ($(strip $(LIBSDLMAIN)),) +ifneq ($(strip $(LIBSDLMAINSRC)),) +$(LIBSDLMAIN) : $(LIBSDLMAINSRC) + cp $< $@ + ranlib $@ +endif +endif + + + +############################################################################# +# DEDICATED SERVER +############################################################################# + +Q3DOBJ = \ + $(B)/ded/sv_bot.o \ + $(B)/ded/sv_client.o \ + $(B)/ded/sv_ccmds.o \ + $(B)/ded/sv_game.o \ + $(B)/ded/sv_init.o \ + $(B)/ded/sv_main.o \ + $(B)/ded/sv_net_chan.o \ + $(B)/ded/sv_snapshot.o \ + $(B)/ded/sv_world.o \ + \ + $(B)/ded/cm_load.o \ + $(B)/ded/cm_patch.o \ + $(B)/ded/cm_polylib.o \ + $(B)/ded/cm_test.o \ + $(B)/ded/cm_trace.o \ + $(B)/ded/cmd.o \ + $(B)/ded/common.o \ + $(B)/ded/cvar.o \ + $(B)/ded/files.o \ + $(B)/ded/md4.o \ + $(B)/ded/msg.o \ + $(B)/ded/net_chan.o \ + $(B)/ded/net_ip.o \ + $(B)/ded/huffman.o \ + \ + $(B)/ded/q_math.o \ + $(B)/ded/q_shared.o \ + \ + $(B)/ded/unzip.o \ + $(B)/ded/vm.o \ + $(B)/ded/vm_interpreted.o \ + \ + $(B)/ded/be_aas_bspq3.o \ + $(B)/ded/be_aas_cluster.o \ + $(B)/ded/be_aas_debug.o \ + $(B)/ded/be_aas_entity.o \ + $(B)/ded/be_aas_file.o \ + $(B)/ded/be_aas_main.o \ + $(B)/ded/be_aas_move.o \ + $(B)/ded/be_aas_optimize.o \ + $(B)/ded/be_aas_reach.o \ + $(B)/ded/be_aas_route.o \ + $(B)/ded/be_aas_routealt.o \ + $(B)/ded/be_aas_sample.o \ + $(B)/ded/be_ai_char.o \ + $(B)/ded/be_ai_chat.o \ + $(B)/ded/be_ai_gen.o \ + $(B)/ded/be_ai_goal.o \ + $(B)/ded/be_ai_move.o \ + $(B)/ded/be_ai_weap.o \ + $(B)/ded/be_ai_weight.o \ + $(B)/ded/be_ea.o \ + $(B)/ded/be_interface.o \ + $(B)/ded/l_crc.o \ + $(B)/ded/l_libvar.o \ + $(B)/ded/l_log.o \ + $(B)/ded/l_memory.o \ + $(B)/ded/l_precomp.o \ + $(B)/ded/l_script.o \ + $(B)/ded/l_struct.o \ + \ + $(B)/ded/null_client.o \ + $(B)/ded/null_input.o \ + $(B)/ded/null_snddma.o \ + \ + $(B)/ded/con_log.o \ + $(B)/ded/sys_main.o + +ifeq ($(ARCH),i386) + Q3DOBJ += \ + $(B)/ded/ftola.o \ + $(B)/ded/snapvectora.o \ + $(B)/ded/matha.o +endif +ifeq ($(ARCH),x86) + Q3DOBJ += \ + $(B)/ded/ftola.o \ + $(B)/ded/snapvectora.o \ + $(B)/ded/matha.o +endif + +ifeq ($(HAVE_VM_COMPILED),true) + ifeq ($(ARCH),i386) + Q3DOBJ += $(B)/ded/vm_x86.o + endif + ifeq ($(ARCH),x86) + Q3DOBJ += $(B)/ded/vm_x86.o + endif + ifeq ($(ARCH),x86_64) + Q3DOBJ += $(B)/ded/vm_x86_64.o $(B)/ded/vm_x86_64_assembler.o + endif + ifeq ($(ARCH),ppc) + Q3DOBJ += $(B)/ded/vm_powerpc.o $(B)/ded/vm_powerpc_asm.o + endif + ifeq ($(ARCH),ppc64) + Q3DOBJ += $(B)/ded/vm_powerpc.o $(B)/ded/vm_powerpc_asm.o + endif + ifeq ($(ARCH),sparc) + Q3DOBJ += $(B)/ded/vm_sparc.o + endif +endif + +ifeq ($(PLATFORM),mingw32) + Q3DOBJ += \ + $(B)/ded/win_resource.o \ + $(B)/ded/sys_win32.o \ + $(B)/ded/con_win32.o +else + Q3DOBJ += \ + $(B)/ded/sys_unix.o \ + $(B)/ded/con_tty.o +endif + +$(B)/Reactionded.$(ARCH)$(BINEXT): $(Q3DOBJ) + $(echo_cmd) "LD $@" + $(Q)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(Q3DOBJ) $(LIBS) + + + +############################################################################# +## BASEQ3 CGAME +############################################################################# + +Q3CGOBJ_ = \ + $(B)/baseq3/cgame/cg_main.o \ + $(B)/baseq3/cgame/bg_misc.o \ + $(B)/baseq3/cgame/bg_pmove.o \ + $(B)/baseq3/cgame/bg_slidemove.o \ + $(B)/baseq3/cgame/bg_lib.o \ + $(B)/baseq3/cgame/cg_consolecmds.o \ + $(B)/baseq3/cgame/cg_draw.o \ + $(B)/baseq3/cgame/cg_drawtools.o \ + $(B)/baseq3/cgame/cg_effects.o \ + $(B)/baseq3/cgame/cg_ents.o \ + $(B)/baseq3/cgame/cg_event.o \ + $(B)/baseq3/cgame/cg_info.o \ + $(B)/baseq3/cgame/cg_localents.o \ + $(B)/baseq3/cgame/cg_marks.o \ + $(B)/baseq3/cgame/cg_players.o \ + $(B)/baseq3/cgame/cg_playerstate.o \ + $(B)/baseq3/cgame/cg_predict.o \ + $(B)/baseq3/cgame/cg_scoreboard.o \ + $(B)/baseq3/cgame/cg_servercmds.o \ + $(B)/baseq3/cgame/cg_snapshot.o \ + $(B)/baseq3/cgame/cg_view.o \ + $(B)/baseq3/cgame/cg_weapons.o \ + \ + $(B)/baseq3/qcommon/q_math.o \ + $(B)/baseq3/qcommon/q_shared.o + +Q3CGOBJ = $(Q3CGOBJ_) $(B)/baseq3/cgame/cg_syscalls.o +Q3CGVMOBJ = $(Q3CGOBJ_:%.o=%.asm) + +$(B)/baseq3/cgame$(ARCH).$(SHLIBEXT): $(Q3CGOBJ) + $(echo_cmd) "LD $@" + $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(Q3CGOBJ) + +$(B)/baseq3/vm/cgame.qvm: $(Q3CGVMOBJ) $(CGDIR)/cg_syscalls.asm $(Q3ASM) + $(echo_cmd) "Q3ASM $@" + $(Q)$(Q3ASM) -o $@ $(Q3CGVMOBJ) $(CGDIR)/cg_syscalls.asm + +############################################################################# +## MISSIONPACK CGAME +############################################################################# + +MPCGOBJ_ = \ + $(B)/missionpack/cgame/cg_main.o \ + $(B)/missionpack/cgame/bg_misc.o \ + $(B)/missionpack/cgame/bg_pmove.o \ + $(B)/missionpack/cgame/bg_slidemove.o \ + $(B)/missionpack/cgame/bg_lib.o \ + $(B)/missionpack/cgame/cg_consolecmds.o \ + $(B)/missionpack/cgame/cg_newdraw.o \ + $(B)/missionpack/cgame/cg_draw.o \ + $(B)/missionpack/cgame/cg_drawtools.o \ + $(B)/missionpack/cgame/cg_effects.o \ + $(B)/missionpack/cgame/cg_ents.o \ + $(B)/missionpack/cgame/cg_event.o \ + $(B)/missionpack/cgame/cg_info.o \ + $(B)/missionpack/cgame/cg_localents.o \ + $(B)/missionpack/cgame/cg_marks.o \ + $(B)/missionpack/cgame/cg_players.o \ + $(B)/missionpack/cgame/cg_playerstate.o \ + $(B)/missionpack/cgame/cg_predict.o \ + $(B)/missionpack/cgame/cg_scoreboard.o \ + $(B)/missionpack/cgame/cg_servercmds.o \ + $(B)/missionpack/cgame/cg_snapshot.o \ + $(B)/missionpack/cgame/cg_view.o \ + $(B)/missionpack/cgame/cg_weapons.o \ + $(B)/missionpack/ui/ui_shared.o \ + \ + $(B)/missionpack/qcommon/q_math.o \ + $(B)/missionpack/qcommon/q_shared.o + +MPCGOBJ = $(MPCGOBJ_) $(B)/missionpack/cgame/cg_syscalls.o +MPCGVMOBJ = $(MPCGOBJ_:%.o=%.asm) + +$(B)/missionpack/cgame$(ARCH).$(SHLIBEXT): $(MPCGOBJ) + $(echo_cmd) "LD $@" + $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(MPCGOBJ) + +$(B)/missionpack/vm/cgame.qvm: $(MPCGVMOBJ) $(CGDIR)/cg_syscalls.asm $(Q3ASM) + $(echo_cmd) "Q3ASM $@" + $(Q)$(Q3ASM) -o $@ $(MPCGVMOBJ) $(CGDIR)/cg_syscalls.asm + + + +############################################################################# +## BASEQ3 GAME +############################################################################# + +Q3GOBJ_ = \ + $(B)/baseq3/game/g_main.o \ + $(B)/baseq3/game/ai_chat.o \ + $(B)/baseq3/game/ai_cmd.o \ + $(B)/baseq3/game/ai_dmnet.o \ + $(B)/baseq3/game/ai_dmq3.o \ + $(B)/baseq3/game/ai_main.o \ + $(B)/baseq3/game/ai_team.o \ + $(B)/baseq3/game/ai_vcmd.o \ + $(B)/baseq3/game/bg_misc.o \ + $(B)/baseq3/game/bg_pmove.o \ + $(B)/baseq3/game/bg_slidemove.o \ + $(B)/baseq3/game/bg_lib.o \ + $(B)/baseq3/game/g_active.o \ + $(B)/baseq3/game/g_arenas.o \ + $(B)/baseq3/game/g_bot.o \ + $(B)/baseq3/game/g_client.o \ + $(B)/baseq3/game/g_cmds.o \ + $(B)/baseq3/game/g_combat.o \ + $(B)/baseq3/game/g_items.o \ + $(B)/baseq3/game/g_mem.o \ + $(B)/baseq3/game/g_misc.o \ + $(B)/baseq3/game/g_missile.o \ + $(B)/baseq3/game/g_mover.o \ + $(B)/baseq3/game/g_session.o \ + $(B)/baseq3/game/g_spawn.o \ + $(B)/baseq3/game/g_svcmds.o \ + $(B)/baseq3/game/g_target.o \ + $(B)/baseq3/game/g_team.o \ + $(B)/baseq3/game/g_trigger.o \ + $(B)/baseq3/game/g_utils.o \ + $(B)/baseq3/game/g_weapon.o \ + \ + $(B)/baseq3/qcommon/q_math.o \ + $(B)/baseq3/qcommon/q_shared.o + +Q3GOBJ = $(Q3GOBJ_) $(B)/baseq3/game/g_syscalls.o +Q3GVMOBJ = $(Q3GOBJ_:%.o=%.asm) + +$(B)/baseq3/qagame$(ARCH).$(SHLIBEXT): $(Q3GOBJ) + $(echo_cmd) "LD $@" + $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(Q3GOBJ) + +$(B)/baseq3/vm/qagame.qvm: $(Q3GVMOBJ) $(GDIR)/g_syscalls.asm $(Q3ASM) + $(echo_cmd) "Q3ASM $@" + $(Q)$(Q3ASM) -o $@ $(Q3GVMOBJ) $(GDIR)/g_syscalls.asm + +############################################################################# +## MISSIONPACK GAME +############################################################################# + +MPGOBJ_ = \ + $(B)/missionpack/game/g_main.o \ + $(B)/missionpack/game/ai_chat.o \ + $(B)/missionpack/game/ai_cmd.o \ + $(B)/missionpack/game/ai_dmnet.o \ + $(B)/missionpack/game/ai_dmq3.o \ + $(B)/missionpack/game/ai_main.o \ + $(B)/missionpack/game/ai_team.o \ + $(B)/missionpack/game/ai_vcmd.o \ + $(B)/missionpack/game/bg_misc.o \ + $(B)/missionpack/game/bg_pmove.o \ + $(B)/missionpack/game/bg_slidemove.o \ + $(B)/missionpack/game/bg_lib.o \ + $(B)/missionpack/game/g_active.o \ + $(B)/missionpack/game/g_arenas.o \ + $(B)/missionpack/game/g_bot.o \ + $(B)/missionpack/game/g_client.o \ + $(B)/missionpack/game/g_cmds.o \ + $(B)/missionpack/game/g_combat.o \ + $(B)/missionpack/game/g_items.o \ + $(B)/missionpack/game/g_mem.o \ + $(B)/missionpack/game/g_misc.o \ + $(B)/missionpack/game/g_missile.o \ + $(B)/missionpack/game/g_mover.o \ + $(B)/missionpack/game/g_session.o \ + $(B)/missionpack/game/g_spawn.o \ + $(B)/missionpack/game/g_svcmds.o \ + $(B)/missionpack/game/g_target.o \ + $(B)/missionpack/game/g_team.o \ + $(B)/missionpack/game/g_trigger.o \ + $(B)/missionpack/game/g_utils.o \ + $(B)/missionpack/game/g_weapon.o \ + \ + $(B)/missionpack/qcommon/q_math.o \ + $(B)/missionpack/qcommon/q_shared.o + +MPGOBJ = $(MPGOBJ_) $(B)/missionpack/game/g_syscalls.o +MPGVMOBJ = $(MPGOBJ_:%.o=%.asm) + +$(B)/missionpack/qagame$(ARCH).$(SHLIBEXT): $(MPGOBJ) + $(echo_cmd) "LD $@" + $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(MPGOBJ) + +$(B)/missionpack/vm/qagame.qvm: $(MPGVMOBJ) $(GDIR)/g_syscalls.asm $(Q3ASM) + $(echo_cmd) "Q3ASM $@" + $(Q)$(Q3ASM) -o $@ $(MPGVMOBJ) $(GDIR)/g_syscalls.asm + + + +############################################################################# +## BASEQ3 UI +############################################################################# + +Q3UIOBJ_ = \ + $(B)/baseq3/ui/ui_main.o \ + $(B)/baseq3/ui/bg_misc.o \ + $(B)/baseq3/ui/bg_lib.o \ + $(B)/baseq3/ui/ui_addbots.o \ + $(B)/baseq3/ui/ui_atoms.o \ + $(B)/baseq3/ui/ui_cdkey.o \ + $(B)/baseq3/ui/ui_cinematics.o \ + $(B)/baseq3/ui/ui_confirm.o \ + $(B)/baseq3/ui/ui_connect.o \ + $(B)/baseq3/ui/ui_controls2.o \ + $(B)/baseq3/ui/ui_credits.o \ + $(B)/baseq3/ui/ui_demo2.o \ + $(B)/baseq3/ui/ui_display.o \ + $(B)/baseq3/ui/ui_gameinfo.o \ + $(B)/baseq3/ui/ui_ingame.o \ + $(B)/baseq3/ui/ui_loadconfig.o \ + $(B)/baseq3/ui/ui_menu.o \ + $(B)/baseq3/ui/ui_mfield.o \ + $(B)/baseq3/ui/ui_mods.o \ + $(B)/baseq3/ui/ui_network.o \ + $(B)/baseq3/ui/ui_options.o \ + $(B)/baseq3/ui/ui_playermodel.o \ + $(B)/baseq3/ui/ui_players.o \ + $(B)/baseq3/ui/ui_playersettings.o \ + $(B)/baseq3/ui/ui_preferences.o \ + $(B)/baseq3/ui/ui_qmenu.o \ + $(B)/baseq3/ui/ui_removebots.o \ + $(B)/baseq3/ui/ui_saveconfig.o \ + $(B)/baseq3/ui/ui_serverinfo.o \ + $(B)/baseq3/ui/ui_servers2.o \ + $(B)/baseq3/ui/ui_setup.o \ + $(B)/baseq3/ui/ui_sound.o \ + $(B)/baseq3/ui/ui_sparena.o \ + $(B)/baseq3/ui/ui_specifyserver.o \ + $(B)/baseq3/ui/ui_splevel.o \ + $(B)/baseq3/ui/ui_sppostgame.o \ + $(B)/baseq3/ui/ui_spskill.o \ + $(B)/baseq3/ui/ui_startserver.o \ + $(B)/baseq3/ui/ui_team.o \ + $(B)/baseq3/ui/ui_teamorders.o \ + $(B)/baseq3/ui/ui_video.o \ + \ + $(B)/baseq3/qcommon/q_math.o \ + $(B)/baseq3/qcommon/q_shared.o + +Q3UIOBJ = $(Q3UIOBJ_) $(B)/missionpack/ui/ui_syscalls.o +Q3UIVMOBJ = $(Q3UIOBJ_:%.o=%.asm) + +$(B)/baseq3/ui$(ARCH).$(SHLIBEXT): $(Q3UIOBJ) + $(echo_cmd) "LD $@" + $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(Q3UIOBJ) + +$(B)/baseq3/vm/ui.qvm: $(Q3UIVMOBJ) $(UIDIR)/ui_syscalls.asm $(Q3ASM) + $(echo_cmd) "Q3ASM $@" + $(Q)$(Q3ASM) -o $@ $(Q3UIVMOBJ) $(UIDIR)/ui_syscalls.asm + +############################################################################# +## MISSIONPACK UI +############################################################################# + +MPUIOBJ_ = \ + $(B)/missionpack/ui/ui_main.o \ + $(B)/missionpack/ui/ui_atoms.o \ + $(B)/missionpack/ui/ui_gameinfo.o \ + $(B)/missionpack/ui/ui_players.o \ + $(B)/missionpack/ui/ui_shared.o \ + \ + $(B)/missionpack/ui/bg_misc.o \ + $(B)/missionpack/ui/bg_lib.o \ + \ + $(B)/missionpack/qcommon/q_math.o \ + $(B)/missionpack/qcommon/q_shared.o + +MPUIOBJ = $(MPUIOBJ_) $(B)/missionpack/ui/ui_syscalls.o +MPUIVMOBJ = $(MPUIOBJ_:%.o=%.asm) + +$(B)/missionpack/ui$(ARCH).$(SHLIBEXT): $(MPUIOBJ) + $(echo_cmd) "LD $@" + $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(MPUIOBJ) + +$(B)/missionpack/vm/ui.qvm: $(MPUIVMOBJ) $(UIDIR)/ui_syscalls.asm $(Q3ASM) + $(echo_cmd) "Q3ASM $@" + $(Q)$(Q3ASM) -o $@ $(MPUIVMOBJ) $(UIDIR)/ui_syscalls.asm + + + +############################################################################# +## CLIENT/SERVER RULES +############################################################################# + +$(B)/client/%.o: $(ASMDIR)/%.s + $(DO_AS) + +$(B)/client/%.o: $(CDIR)/%.c + $(DO_CC) + +$(B)/client/%.o: $(SDIR)/%.c + $(DO_CC) + +$(B)/client/%.o: $(CMDIR)/%.c + $(DO_CC) + +$(B)/client/%.o: $(BLIBDIR)/%.c + $(DO_BOT_CC) + +$(B)/client/%.o: $(JPDIR)/%.c + $(DO_CC) + +$(B)/client/%.o: $(SPEEXDIR)/%.c + $(DO_CC) + +$(B)/client/%.o: $(RDIR)/%.c + $(DO_CC) + +$(B)/client/%.o: $(SDLDIR)/%.c + $(DO_CC) + +$(B)/clientsmp/%.o: $(SDLDIR)/%.c + $(DO_SMP_CC) + +$(B)/client/%.o: $(SYSDIR)/%.c + $(DO_CC) + +$(B)/client/%.o: $(SYSDIR)/%.rc + $(DO_WINDRES) + + +$(B)/ded/%.o: $(ASMDIR)/%.s + $(DO_AS) + +$(B)/ded/%.o: $(SDIR)/%.c + $(DO_DED_CC) + +$(B)/ded/%.o: $(CMDIR)/%.c + $(DO_DED_CC) + +$(B)/ded/%.o: $(BLIBDIR)/%.c + $(DO_BOT_CC) + +$(B)/ded/%.o: $(SYSDIR)/%.c + $(DO_DED_CC) + +$(B)/ded/%.o: $(SYSDIR)/%.rc + $(DO_WINDRES) + +$(B)/ded/%.o: $(NDIR)/%.c + $(DO_DED_CC) + +# Extra dependencies to ensure the SVN version is incorporated +ifeq ($(USE_SVN),1) + $(B)/client/cl_console.o : .svn/entries + $(B)/client/common.o : .svn/entries + $(B)/ded/common.o : .svn/entries +endif + + +############################################################################# +## GAME MODULE RULES +############################################################################# + +$(B)/baseq3/cgame/bg_%.o: $(GDIR)/bg_%.c + $(DO_CGAME_CC) + +$(B)/baseq3/cgame/%.o: $(CGDIR)/%.c + $(DO_CGAME_CC) + +$(B)/baseq3/cgame/bg_%.asm: $(GDIR)/bg_%.c $(Q3LCC) + $(DO_CGAME_Q3LCC) + +$(B)/baseq3/cgame/%.asm: $(CGDIR)/%.c $(Q3LCC) + $(DO_CGAME_Q3LCC) + +$(B)/missionpack/cgame/bg_%.o: $(GDIR)/bg_%.c + $(DO_CGAME_CC_MISSIONPACK) + +$(B)/missionpack/cgame/%.o: $(CGDIR)/%.c + $(DO_CGAME_CC_MISSIONPACK) + +$(B)/missionpack/cgame/bg_%.asm: $(GDIR)/bg_%.c $(Q3LCC) + $(DO_CGAME_Q3LCC_MISSIONPACK) + +$(B)/missionpack/cgame/%.asm: $(CGDIR)/%.c $(Q3LCC) + $(DO_CGAME_Q3LCC_MISSIONPACK) + + +$(B)/baseq3/game/%.o: $(GDIR)/%.c + $(DO_GAME_CC) + +$(B)/baseq3/game/%.asm: $(GDIR)/%.c $(Q3LCC) + $(DO_GAME_Q3LCC) + +$(B)/missionpack/game/%.o: $(GDIR)/%.c + $(DO_GAME_CC_MISSIONPACK) + +$(B)/missionpack/game/%.asm: $(GDIR)/%.c $(Q3LCC) + $(DO_GAME_Q3LCC_MISSIONPACK) + + +$(B)/baseq3/ui/bg_%.o: $(GDIR)/bg_%.c + $(DO_UI_CC) + +$(B)/baseq3/ui/%.o: $(Q3UIDIR)/%.c + $(DO_UI_CC) + +$(B)/baseq3/ui/bg_%.asm: $(GDIR)/bg_%.c $(Q3LCC) + $(DO_UI_Q3LCC) + +$(B)/baseq3/ui/%.asm: $(Q3UIDIR)/%.c $(Q3LCC) + $(DO_UI_Q3LCC) + +$(B)/missionpack/ui/bg_%.o: $(GDIR)/bg_%.c + $(DO_UI_CC_MISSIONPACK) + +$(B)/missionpack/ui/%.o: $(UIDIR)/%.c + $(DO_UI_CC_MISSIONPACK) + +$(B)/missionpack/ui/bg_%.asm: $(GDIR)/bg_%.c $(Q3LCC) + $(DO_UI_Q3LCC_MISSIONPACK) + +$(B)/missionpack/ui/%.asm: $(UIDIR)/%.c $(Q3LCC) + $(DO_UI_Q3LCC_MISSIONPACK) + + +$(B)/baseq3/qcommon/%.o: $(CMDIR)/%.c + $(DO_SHLIB_CC) + +$(B)/baseq3/qcommon/%.asm: $(CMDIR)/%.c $(Q3LCC) + $(DO_Q3LCC) + +$(B)/missionpack/qcommon/%.o: $(CMDIR)/%.c + $(DO_SHLIB_CC_MISSIONPACK) + +$(B)/missionpack/qcommon/%.asm: $(CMDIR)/%.c $(Q3LCC) + $(DO_Q3LCC_MISSIONPACK) + + +############################################################################# +# MISC +############################################################################# + +OBJ = $(Q3OBJ) $(Q3POBJ) $(Q3POBJ_SMP) $(Q3DOBJ) \ + $(MPGOBJ) $(Q3GOBJ) $(Q3CGOBJ) $(MPCGOBJ) $(Q3UIOBJ) $(MPUIOBJ) \ + $(MPGVMOBJ) $(Q3GVMOBJ) $(Q3CGVMOBJ) $(MPCGVMOBJ) $(Q3UIVMOBJ) $(MPUIVMOBJ) +TOOLSOBJ = $(LBURGOBJ) $(Q3CPPOBJ) $(Q3RCCOBJ) $(Q3LCCOBJ) $(Q3ASMOBJ) + + +copyfiles: release + @if [ ! -d $(COPYDIR)/baseq3 ]; then echo "You need to set COPYDIR to where your Quake3 data is!"; fi + -$(MKDIR) -p -m 0755 $(COPYDIR)/baseq3 + -$(MKDIR) -p -m 0755 $(COPYDIR)/missionpack + +ifneq ($(BUILD_CLIENT),0) + $(INSTALL) -s -m 0755 $(BR)/Reaction.$(ARCH)$(BINEXT) $(COPYDIR)/Reaction.$(ARCH)$(BINEXT) +endif + +# Don't copy the SMP until it's working together with SDL. +#ifneq ($(BUILD_CLIENT_SMP),0) +# $(INSTALL) -s -m 0755 $(BR)/Reaction-smp.$(ARCH)$(BINEXT) $(COPYDIR)/Reaction-smp.$(ARCH)$(BINEXT) +#endif + +ifneq ($(BUILD_SERVER),0) + @if [ -f $(BR)/Reactionded.$(ARCH)$(BINEXT) ]; then \ + $(INSTALL) -s -m 0755 $(BR)/Reactionded.$(ARCH)$(BINEXT) $(COPYDIR)/Reactionded.$(ARCH)$(BINEXT); \ + fi +endif + +ifneq ($(BUILD_GAME_SO),0) + $(INSTALL) -s -m 0755 $(BR)/baseq3/cgame$(ARCH).$(SHLIBEXT) \ + $(COPYDIR)/baseq3/. + $(INSTALL) -s -m 0755 $(BR)/baseq3/qagame$(ARCH).$(SHLIBEXT) \ + $(COPYDIR)/baseq3/. + $(INSTALL) -s -m 0755 $(BR)/baseq3/ui$(ARCH).$(SHLIBEXT) \ + $(COPYDIR)/baseq3/. + ifneq ($(BUILD_MISSIONPACK),0) + -$(MKDIR) -p -m 0755 $(COPYDIR)/missionpack + $(INSTALL) -s -m 0755 $(BR)/missionpack/cgame$(ARCH).$(SHLIBEXT) \ + $(COPYDIR)/missionpack/. + $(INSTALL) -s -m 0755 $(BR)/missionpack/qagame$(ARCH).$(SHLIBEXT) \ + $(COPYDIR)/missionpack/. + $(INSTALL) -s -m 0755 $(BR)/missionpack/ui$(ARCH).$(SHLIBEXT) \ + $(COPYDIR)/missionpack/. + endif +endif + +clean: clean-debug clean-release +ifeq ($(PLATFORM),mingw32) + @$(MAKE) -C $(NSISDIR) clean +else + @$(MAKE) -C $(LOKISETUPDIR) clean +endif + +clean-debug: + @$(MAKE) clean2 B=$(BD) + +clean-release: + @$(MAKE) clean2 B=$(BR) + +clean2: + @echo "CLEAN $(B)" + @rm -f $(OBJ) + @rm -f $(OBJ_D_FILES) + @rm -f $(TARGETS) + +toolsclean: toolsclean-debug toolsclean-release + +toolsclean-debug: + @$(MAKE) toolsclean2 B=$(BD) + +toolsclean-release: + @$(MAKE) toolsclean2 B=$(BR) + +toolsclean2: + @echo "TOOLS_CLEAN $(B)" + @rm -f $(TOOLSOBJ) + @rm -f $(TOOLSOBJ_D_FILES) + @rm -f $(LBURG) $(DAGCHECK_C) $(Q3RCC) $(Q3CPP) $(Q3LCC) $(Q3ASM) + +distclean: clean toolsclean + @rm -rf $(BUILD_DIR) + +installer: release +ifeq ($(PLATFORM),mingw32) + @$(MAKE) VERSION=$(VERSION) -C $(NSISDIR) V=$(V) +else + @$(MAKE) VERSION=$(VERSION) -C $(LOKISETUPDIR) V=$(V) +endif + +dist: + rm -rf Reaction-$(VERSION) + svn export . Reaction-$(VERSION) + tar --owner=root --group=root --force-local -cjf Reaction-$(VERSION).tar.bz2 Reaction-$(VERSION) + rm -rf Reaction-$(VERSION) + +############################################################################# +# DEPENDENCIES +############################################################################# + +OBJ_D_FILES=$(filter %.d,$(OBJ:%.o=%.d)) +TOOLSOBJ_D_FILES=$(filter %.d,$(TOOLSOBJ:%.o=%.d)) +-include $(OBJ_D_FILES) $(TOOLSOBJ_D_FILES) + +.PHONY: all clean clean2 clean-debug clean-release copyfiles \ + debug default dist distclean installer makedirs \ + release targets \ + toolsclean toolsclean2 toolsclean-debug toolsclean-release diff --git a/reaction/engine/NOTTODO b/reaction/engine/NOTTODO new file mode 100644 index 00000000..0db15474 --- /dev/null +++ b/reaction/engine/NOTTODO @@ -0,0 +1 @@ +http://wiki.ioquake3.org/NotToDo diff --git a/reaction/engine/README b/reaction/engine/README new file mode 100644 index 00000000..12219161 --- /dev/null +++ b/reaction/engine/README @@ -0,0 +1,536 @@ + ,---------------------------------------. + | _ _ ____ | + | (_)___ __ _ _ _ __ _| |_____|__ / | + | | / _ \/ _` | || / _` | / / -_)|_ \ | + | |_\___/\__, |\_,_\__,_|_\_\___|___/ | + | |_| | + | | + `---------- http://ioquake3.org --------' + +The intent of this project is to provide a baseline Quake 3 which may be used +for further development and baseq3 fun. +Some of the major features currently implemented are: + + * SDL backend + * OpenAL sound API support (multiple speaker support and better sound + quality) + * Full x86_64 support on Linux + * VoIP support, both in-game and external support through Mumble. + * MinGW compilation support on Windows and cross compilation support on Linux + * AVI video capture of demos + * Much improved console autocompletion + * Persistent console history + * Colorized terminal output + * Optional Ogg Vorbis support + * Much improved QVM tools + * Support for various esoteric operating systems + * cl_guid support + * HTTP/FTP download redirection (using cURL) + * Multiuser support on Windows systems (user specific game data + is stored in "%APPDATA%\Quake3") + * PNG support + * Many, many bug fixes + +The map editor and associated compiling tools are not included. We suggest you +use a modern copy from http://www.qeradiant.com/. + +The original id software readme that accompanied the Q3 source release has been +renamed to id-readme.txt so as to prevent confusion. Please refer to the +web-site for updated status. + + +--------------------------------------------- Compilation and installation ----- + +For *nix + 1. Change to the directory containing this readme. + 2. Run 'make'. + +For Windows, + 1. Please refer to the excellent instructions here: + http://wiki.ioquake3.org/Building_ioquake3 + +For Mac OS X, building a Universal Binary + 1. Install MacOSX SDK packages from XCode. For maximum compatibility, + install MacOSX10.4u.sdk and MacOSX10.3.9.sdk, and MacOSX10.2.8.sdk. + 2. Change to the directory containing this README file. + 3. Run './make-macosx-ub.sh' + 4. Copy the resulting ioquake3.app in /build/release-darwin-ub to your + /Applications/ioquake3 folder. + +Installation, for *nix + 1. Set the COPYDIR variable in the shell to be where you installed Quake 3 + to. By default it will be /usr/local/games/quake3 if you haven't set it. + This is the path as used by the original Linux Q3 installer and subsequent + point releases. + 2. Run 'make copyfiles'. + +It is also possible to cross compile for Windows under *nix using MinGW. A +script is available to build a cross compilation environment from +http://www.libsdl.org/extras/win32/cross/build-cross.sh. The gcc/binutils +version numbers that the script downloads may need to be altered. +Alternatively, your distribution may have mingw32 packages available. On +debian/Ubuntu, these are mingw32, mingw32-runtime and mingw32-binutils. Cross +compiling is simply a case of using './cross-make-mingw.sh' in place of 'make', +though you may find you need to change the value of the variables in this +script to match your environment. + +The following variables may be set, either on the command line or in +Makefile.local: + + CFLAGS - use this for custom CFLAGS + V - set to show cc command line when building + DEFAULT_BASEDIR - extra path to search for baseq3 and such + BUILD_SERVER - build the 'ioq3ded' server binary + BUILD_CLIENT - build the 'ioquake3' client binary + BUILD_CLIENT_SMP - build the 'ioquake3-smp' client binary + BUILD_GAME_SO - build the game shared libraries + BUILD_GAME_QVM - build the game qvms + BUILD_STANDALONE - build binaries suited for stand-alone games + USE_OPENAL - use OpenAL where available + USE_OPENAL_DLOPEN - link with OpenAL at runtime + USE_CURL - use libcurl for http/ftp download support + USE_CURL_DLOPEN - link with libcurl at runtime + USE_CODEC_VORBIS - enable Ogg Vorbis support + USE_LOCAL_HEADERS - use headers local to ioq3 instead of system ones + COPYDIR - the target installation directory + +The defaults for these variables differ depending on the target platform. + + +------------------------------------------------------------------ Console ----- + +New cvars + cl_autoRecordDemo - record a new demo on each map change + cl_aviFrameRate - the framerate to use when capturing video + cl_aviMotionJpeg - use the mjpeg codec when capturing video + cl_guidServerUniq - makes cl_guid unique for each server + cl_cURLLib - filename of cURL library to load + cl_consoleKeys - space delimited list of key names or + characters that toggle the console + + s_useOpenAL - use the OpenAL sound backend if available + s_alPrecache - cache OpenAL sounds before use + s_alGain - the value of AL_GAIN for each source + s_alSources - the total number of sources (memory) to + allocate + s_alDopplerFactor - the value passed to alDopplerFactor + s_alDopplerSpeed - the value passed to alDopplerVelocity + s_alMinDistance - the value of AL_REFERENCE_DISTANCE for + each source + s_alMaxDistance - the maximum distance before sounds start + to become inaudible. + s_alRolloff - the value of AL_ROLLOFF_FACTOR for each + source + s_alGraceDistance - after having passed MaxDistance, length + until sounds are completely inaudible + s_alDriver - which OpenAL library to use + s_alDevice - which OpenAL device to use + s_alAvailableDevices - list of available OpenAL devices + + s_sdlBits - SDL bit resolution + s_sdlSpeed - SDL sample rate + s_sdlChannels - SDL number of channels + s_sdlDevSamps - SDL DMA buffer size override + s_sdlMixSamps - SDL mix buffer size override + + com_ansiColor - enable use of ANSI escape codes in the tty + com_altivec - enable use of altivec on PowerPC systems + com_standalone - Run in standalone mode + com_maxfpsUnfocused - Maximum frames per second when unfocused + com_maxfpsMinimized - Maximum frames per second when minimized + s_backend - read only, indicates the current sound + backend + s_muteWhenMinimized - mute sound when minimized + in_joystickNo - select which joystick to use + in_keyboardDebug - print keyboard debug info + r_ext_texture_filter_anisotropic - anisotropic texture filtering + sv_dlURL - the base of the HTTP or FTP site that + holds custom pk3 files for your server + + net_ip6 - IPv6 address to bind to + net_port6 - port to bind to using the ipv6 address + net_enabled - enable networking, bitmask. Add up + number for option to enable it: + enable ipv4 networking: 1 + enable ipv6 networking: 2 + prioritise ipv6 over ipv4: 4 + disable multicast support: 8 + net_mcast6addr - multicast address to use for scanning for + ipv6 servers on the local network + net_mcastiface - outgoing interface to use for scan + + r_zProj - distance of observer camera to projection + plane in quake3 standard units + r_greyscale - render black and white images + r_stereoEnabled - enable stereo rendering for techniques + like shutter glasses (untested) + r_anaglyphMode - Enable rendering of anaglyph images + red-cyan glasses: 1 + red-blue: 2 + red-green: 3 + To swap the colors for left and right eye + just add 3 to the value for the wanted + color combination. For red-blue and + red-green you probably want to enable + r_greyscale + r_stereoSeparation - Control eye separation. Resulting + separation is r_zProj divided by this + value in quake3 standard units. + See also + http://wiki.ioquake3.org/Stereo_Rendering + for more information + r_sdlDriver - read only, indicates the SDL driver + backend being used + sv_banFile - Name of the file that is used for storing + the server bans. + +New commands + video [filename] - start video capture (use with demo command) + stopvideo - stop video capture + + print - print out the contents of a cvar + + banaddr - ban an ip address range from joining a game on this + server, valid is either playernum or CIDR + notation address range. + exceptaddr - exempt an ip address range from a ban. + bandel - delete ban (either range or ban number) + exceptdel - delete exception (either range or exception number) + listbans - list all currently active bans and exceptions + rehashbans - reload the banlist from serverbans.dat + flushbans - delete all bans + + net_restart - restart network subsystem to change latched settings + +------------------------------------------------------------ Miscellaneous ----- + +Using shared libraries instead of qvm + To force Q3 to use shared libraries instead of qvms run it with the following + parameters: +set sv_pure 0 +set vm_cgame 0 +set vm_game 0 +set vm_ui 0 + +Using Demo Data Files + Copy demoq3/pak0.pk3 from the demo installer to your baseq3 directory. The + qvm files in this pak0.pk3 will not work, so you have to use the native + shared libraries or qvms from this project. To use the new qvms, they must be + put into a pk3 file. A pk3 file is just a zip file, so any compression tool + that can create such files will work. The shared libraries should already be + in the correct place. Use the instructions above to use them. + + Please bear in mind that you will not be able to play online using the demo + data, nor is it something that we like to spend much time maintaining or + supporting. + +64bit mods + If you wish to compile external mods as shared libraries on a 64bit platform, + and the mod source is derived from the id Q3 SDK, you will need to modify the + interface code a little. Open the files ending in _syscalls.c and change + every instance of int to intptr_t in the declaration of the syscall function + pointer and the dllEntry function. Also find the vmMain function for each + module (usually in cg_main.c g_main.c etc.) and similarly replace the return + value in the prototype with intptr_t (arg0, arg1, ...stay int). + + Add the following code snippet to q_shared.h: + + #ifdef Q3_VM + typedef int intptr_t; + #else + #include + #endif + + Note if you simply wish to run mods on a 64bit platform you do not need to + recompile anything since by default Q3 uses a virtual machine system. + +Creating mods compatible with Q3 1.32b + If you're using this package to create mods for the last official release of + Q3, it is necessary to pass the commandline option '-vq3' to your invocation + of q3asm. This is because by default q3asm outputs an updated qvm format that + is necessary to fix a bug involving the optimizing pass of the x86 vm JIT + compiler. + +Creating standalone games + Have you finished the daunting task of removing all dependencies on the Q3 + game data? You probably now want to give your users the opportunity to play + the game without owning a copy of Q3, which consequently means removing cd-key + and authentication server checks. In addition to being a straightforward Q3 + client, ioquake3 also purports to be a reliable and stable code base on which + to base your game project. + + However, before you start compiling your own version of ioquake3, you have to + ask yourself: Have we changed or will we need to change anything of importance + in the engine? + + If your answer to this question is "no", it probably makes no sense to build + your own binaries. Instead, you can just use the pre-built binaries on the + website. Just make sure the game is called with: + + +set com_standalone 1 +set fs_game + + in any links/scripts you install for your users to start the game. Note that + the com_standalone setting is rendered ineffective, if the binary detects pk3 + files in the directory "baseq3", so you cannot use that one as game dir. + + If you really changed parts that would make vanilla ioquake3 incompatible with + your mod, we have included another way to conveniently build a stand-alone + binary. Just run make with the option BUILD_STANDALONE=1. Don't forget to edit + the PRODUCT_NAME and subsequent #defines in qcommon/q_shared.h with + information appropriate for your project. + + While a lot of work has been put into ioquake3 that you can benefit from free + of charge, it does not mean that you have no obligations to fulfil. Please be + aware that as soon as you start distributing your game with an engine based on + our sources we expect you to fully comply with the requirements as stated in + the GPL. That includes making sources and modifications you made to the + ioquake3 engine as well as the game-code used to compile the .qvm files for + the game logic freely available to everyone. Furthermore, note that the "QIIIA + Game Source License" prohibits distribution of mods that are intended to + operate on a version of Q3 not sanctioned by id software: + + "with this Agreement, ID grants to you the non-exclusive and limited right + to distribute copies of the Software ... for operation only with the full + version of the software game QUAKE III ARENA" + + This means that if you're creating a standalone game, you cannot use said + license on any portion of the product. As the only other license this code has + been released under is the GPL, this is the only option. + + This does NOT mean that you cannot market this game commercially. The GPL does + not prohibit commercial exploitation and all assets (e.g. textures, sounds, + maps) created by yourself are your property and can be sold like every other + game you find in stores. + +cl_guid Support + cl_guid is a cvar which is part of the client's USERINFO string. Its value + is a 32 character string made up of [a-f] and [0-9] characters. This + value is pseudo-unique for every player. Id's Quake 3 Arena client also + sets cl_guid, but only if Punkbuster is enabled on the client. + + If cl_guidServerUniq is non-zero (the default), then this value is also + pseudo-unique for each server a client connects to (based on IP:PORT of + the server). + + The purpose of cl_guid is to add an identifier for each player on + a server. This value can be reset by the client at any time so it's not + useful for blocking access. However, it can have at least two uses in + your mod's game code: + 1) improve logging to allow statistical tools to index players by more + than just name + 2) granting some weak admin rights to players without requiring passwords + +Using HTTP/FTP Download Support (Server) + You can enable redirected downloads on your server even if it's not + an ioquake3 server. You simply need to use the 'sets' command to put + the sv_dlURL cvar into your SERVERINFO string and ensure sv_allowDownloads + is set to 1 + + sv_dlURL is the base of the URL that contains your custom .pk3 files + the client will append both fs_game and the filename to the end of + this value. For example, if you have sv_dlURL set to + "http://ioquake3.org", fs_game is "baseq3", and the client is + missing "test.pk3", it will attempt to download from the URL + "http://ioquake3.org/baseq3/test.pk3" + + sv_allowDownload's value is now a bitmask made up of the following + flags: + 1 - ENABLE + 2 - do not use HTTP/FTP downloads + 4 - do not use UDP downloads + 8 - do not ask the client to disconnect when using HTTP/FTP + + Server operators who are concerned about potential "leeching" from their + HTTP servers from other ioquake3 servers can make use of the HTTP_REFERER + that ioquake3 sets which is "ioQ3://{SERVER_IP}:{SERVER_PORT}". For, + example, Apache's mod_rewrite can restrict access based on HTTP_REFERER. + +Using HTTP/FTP Download Support (Client) + Simply setting cl_allowDownload to 1 will enable HTTP/FTP downloads + assuming ioquake3 was compiled with USE_CURL=1 (the default). + like sv_allowDownload, cl_allowDownload also uses a bitmask value + supporting the following flags: + 1 - ENABLE + 2 - do not use HTTP/FTP downloads + 4 - do not use UDP downloads + + When ioquake3 is built with USE_CURL_DLOPEN=1 (default on some platforms), + it will use the value of the cvar cl_cURLLib as the filename of the cURL + library to dynamically load. + +Multiuser Support on Windows systems + On Windows, all user specific files such as autogenerated configuration, + demos, videos, screenshots, and autodownloaded pk3s are now saved in a + directory specific to the user who is running ioquake3. + + On NT-based such as Windows XP, this is usually a directory named: + "C:\Documents and Settings\%USERNAME%\Application Data\Quake3\" + + Windows 95, Windows 98, and Windows ME will use a directory like: + "C:\Windows\Application Data\Quake3" + in single-user mode, or: + "C:\Windows\Profiles\%USERNAME%\Application Data\Quake3" + if multiple logins have been enabled. + + In order to access this directory more easily, the installer may create a + Shortcut which has its target set to: + "%APPDATA%\Quake3\" + This Shortcut would work for all users on the system regardless of the + locale settings. Unfortunately, this environment variable is only + present on Windows NT based systems. + + You can revert to the old single-user behaviour by setting the fs_homepath + cvar to the directory where ioquake3 is installed. For example: + ioquake3.exe +set fs_homepath "c:\ioquake3" + Note that this cvar MUST be set as a command line parameter. + +SDL Keyboard Differences + ioquake3 clients have different keyboard behaviour compared to the original + Quake3 clients. + + * "Caps Lock" and "Num Lock" can not be used as normal binds since they + do not send a KEYUP event until the key is pressed again. + + * SDL > 1.2.9 does not support disabling dead key recognition. In order to + send dead key characters (e.g. ~, ', `, and ^), you must key a Space (or + sometimes the same character again) after the character to send it on + many international keyboard layouts. + + * The SDL client supports many more keys than the original Quake3 client. + For example the keys: "Windows", "SysReq", "ScrollLock", and "Break". + For non-US keyboards, all of the so called "World" keys are now supported + as well as F13, F14, F15, and the country-specific mode/meta keys. + + On many international layouts the default console toggle keys are also dead + keys, meaning that dropping the console potentially results in + unintentionally initiating the keying of a dead key. Futhermore SDL 1.2's + dead key support is broken by design and Q3 doesn't support non-ASCII text + entry, so the chances are you won't get the correct character anyway. + + If you use such a keyboard layout, you can set the cvar cl_consoleKeys. This + is a space delimited list of key names that will toggle the console. The key + names are the usual Q3 names e.g. "~", "`", "c", "BACKSPACE", "PAUSE", + "WINDOWS" etc. It's also possible to use ASCII characters, by hexadecimal + number. Some example values for cl_consoleKeys: + + "~ ` 0x7e 0x60" Toggle on ~ or ` (the default) + "WINDOWS" Toggle on the Windows key + "c" Toggle on the c key + "0x43" Toggle on the C character (Shift-c) + "PAUSE F1 PGUP" Toggle on the Pause, F1 or Page Up keys + + Note that when you elect a set of console keys or characters, they cannot + then be used for binding, nor will they generate characters when entering + text. Also, in addition to the nominated console keys, Shift-ESC is hard + coded to always toggle the console. + +Mouse Input On Windows + ioq3 uses SDL to abstract away as much as possible from platform specific + implementation details. Unfortunately, SDL 1.2 suffers from a number of bugs + and limitations with respect to mouse input on the Windows platform. We + provide a patch against the SDL subversion 1.2 branch which fixes the + following problems: + + * DirectX (and thus DirectInput) driver not functional when using an + OpenGL SDL_Surface. + + * DirectX (and thus DirectInput) driver not functional in windowed mode. + + * Mouse buttons 4-7 unusable with the DirectX driver due to DirectInput 5 + not exposing the required functionality. Use DirectInput 7 instead. + + * Low quality mouse input data when using the windib driver due to use of + WM_MOUSEMOVE events. Use GetCursorPos API call instead. + + The patch can be found in misc/sdl-win32-fixes.diff. + +PNG support + ioquake3 supports the use of PNG (Portable Network Graphic) images as + textures. It should be noted that the use of such images in a map will + result in missing placeholder textures where the map is used with the id + Quake 3 client or earlier versions of ioquake3. + + Recent versions of GtkRadiant and q3map2 support PNG images without + modification. However GtkRadiant is not aware that PNG textures are supported + by ioquake3. To change this behaviour open the file 'q3.game' in the 'games' + directory of the GtkRadiant base directory with an editor and change the + line: + + texturetypes="tga jpg" + + to + + texturetypes="tga jpg png" + + Restart GtkRadiant and PNG textures are now available. + +Building with MinGW for pre Windows XP + IPv6 support requires a header named "wspiapi.h" to abstract away from + differences in earlier versions of Windows' IPv6 stack. There is no MinGW + equivalent of this header and the Microsoft version is obviously not + redistributable, so in its absence we're forced to require Windows XP. + However if this header is acquired separately and placed in the qcommon/ + directory, this restriction is lifted. + + +------------------------------------------------------------- Contributing ----- + +Please send all patches to bugzilla (https://bugzilla.icculus.org), or join the +mailing list (quake3-subscribe@icculus.org) and submit your patch there. The +best case scenario is that you submit your patch to bugzilla, and then post the +URL to the mailing list. + +The focus for ioq3 is to develop a stable base suitable for further development +and provide players with the same Quake 3 experience they've had for years. As +such ioq3 does not have any significant graphical enhancements and none are +planned at this time. However, improved graphics and sound patches will be +accepted as long as they are entirely optional, do not require new media and +are off by default. + + +--------------------------------------------- Building Official Installers ----- + +We need help getting automated installers on all the platforms that ioquake3 +supports. We don't neccesarily care about all the installers being identical, +but we have some general guidelines: + + * Please include the id patch pk3s in your installer, which are available + from http://ioquake3.org/patch-data/ subject to agreement to the id + EULA. Your installer shall also ask the user to agree to this EULA (which + is in the /web/include directory for your convenience) and subsequently + refuse to continue the installation of the patch pk3s and pak0.pk3 if they + do not. + + * Please don't require pak0.pk3, since not everyone using the engine + plans on playing Quake 3 Arena on it. It's fine to (optionally) assist the + user in copying the file or tell them how. + + * It is fine to just install the binaries without requiring id EULA agreement, + providing pak0.pk3 and the patch pk3s are not refered to or included in the + installer. + + * Please include at least an SDL so/dylib/dll on every platform. + + * Please include an OpenAL so/dylib/dll, since every platform should be using + it by now. + + * Please contact the mailing list when you've made your installer. + + * Please be prepared to alter your installer on the whim of the maintainers. + + * Your installer will be mirrored to an "official" directory, thus making it + a done deal. + +------------------------------------------------------------------ Credits ----- + +Maintainers + Ludwig Nussel + Thilo Schulz + Tim Angus + Tony J. White + Zachary J. Slater + +Significant contributions from + Ryan C. Gordon + Andreas Kohn + Joerg Dietrich + Stuart Dalton + Vincent S. Cojot + optical + Aaron Gyes diff --git a/reaction/engine/TODO b/reaction/engine/TODO new file mode 100644 index 00000000..5a3b5459 --- /dev/null +++ b/reaction/engine/TODO @@ -0,0 +1 @@ +http://wiki.ioquake3.org/Ioquake3_Road_Map diff --git a/reaction/engine/code/AL/VERSION b/reaction/engine/code/AL/VERSION new file mode 100644 index 00000000..1af4e3fb --- /dev/null +++ b/reaction/engine/code/AL/VERSION @@ -0,0 +1,16 @@ +This file identifies the version of the AL headers in this directory. If you +change or update the AL headers in any way, please make sure you also update +this file. + +SVN revision >= 402 +------------------- +Headers are from OpenAL CVS 6th August 2005: +$ cvs -d:pserver:guest@opensource.creative.com:/usr/local/cvs-repository +login +(use password "guest") +$ cvs -d:pserver:guest@opensource.creative.com:/usr/local/cvs-repository +co -D "6 Aug 2005" openal + +SVN revision >= 374 +------------------- +Standard OpenAL 1.0 headers diff --git a/reaction/engine/code/AL/al.h b/reaction/engine/code/AL/al.h new file mode 100644 index 00000000..fd9a5375 --- /dev/null +++ b/reaction/engine/code/AL/al.h @@ -0,0 +1,506 @@ +#ifndef __al_h_ +#define __al_h_ + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ +#include "altypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* WIN32, not Xbox */ +#ifdef _WIN32 +#ifndef _XBOX +#ifdef _OPENAL32LIB +#define ALAPI __declspec(dllexport) +#else +#define ALAPI __declspec(dllimport) +#endif +#define ALAPIENTRY __cdecl +#define AL_CALLBACK +#endif +#endif + +#ifdef TARGET_OS_MAC +#if TARGET_OS_MAC +#pragma export on +#endif +#endif + +#ifndef ALAPI +#define ALAPI +#endif + +#ifndef ALAPIENTRY +#define ALAPIENTRY +#endif + +#ifndef CALLBACK +#define AL_CALLBACK +#endif + +#define OPENAL + +#ifndef AL_NO_PROTOTYPES + +/* + * Renderer State management + */ +ALAPI void ALAPIENTRY alEnable( ALenum capability ); + +ALAPI void ALAPIENTRY alDisable( ALenum capability ); + +ALAPI ALboolean ALAPIENTRY alIsEnabled( ALenum capability ); + + +/* + * State retrieval + */ +ALAPI const ALchar* ALAPIENTRY alGetString( ALenum param ); + +ALAPI void ALAPIENTRY alGetBooleanv( ALenum param, ALboolean* data ); + +ALAPI void ALAPIENTRY alGetIntegerv( ALenum param, ALint* data ); + +ALAPI void ALAPIENTRY alGetFloatv( ALenum param, ALfloat* data ); + +ALAPI void ALAPIENTRY alGetDoublev( ALenum param, ALdouble* data ); + +ALAPI ALboolean ALAPIENTRY alGetBoolean( ALenum param ); + +ALAPI ALint ALAPIENTRY alGetInteger( ALenum param ); + +ALAPI ALfloat ALAPIENTRY alGetFloat( ALenum param ); + +ALAPI ALdouble ALAPIENTRY alGetDouble( ALenum param ); + + +/* + * Error support. + * Obtain the most recent error generated in the AL state machine. + */ +ALAPI ALenum ALAPIENTRY alGetError( ALvoid ); + + +/* + * Extension support. + * Query for the presence of an extension, and obtain any appropriate + * function pointers and enum values. + */ +ALAPI ALboolean ALAPIENTRY alIsExtensionPresent( const ALchar* extname ); + +ALAPI void* ALAPIENTRY alGetProcAddress( const ALchar* fname ); + +ALAPI ALenum ALAPIENTRY alGetEnumValue( const ALchar* ename ); + + +/* + * LISTENER + * Listener represents the location and orientation of the + * 'user' in 3D-space. + * + * Properties include: - + * + * Gain AL_GAIN ALfloat + * Position AL_POSITION ALfloat[3] + * Velocity AL_VELOCITY ALfloat[3] + * Orientation AL_ORIENTATION ALfloat[6] (Forward then Up vectors) +*/ + +/* + * Set Listener parameters + */ +ALAPI void ALAPIENTRY alListenerf( ALenum param, ALfloat value ); + +ALAPI void ALAPIENTRY alListener3f( ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); + +ALAPI void ALAPIENTRY alListenerfv( ALenum param, const ALfloat* values ); + +ALAPI void ALAPIENTRY alListeneri( ALenum param, ALint value ); + +ALAPI void ALAPIENTRY alListener3i( ALenum param, ALint value1, ALint value2, ALint value3 ); + +ALAPI void ALAPIENTRY alListeneriv( ALenum param, const ALint* values ); + +/* + * Get Listener parameters + */ +ALAPI void ALAPIENTRY alGetListenerf( ALenum param, ALfloat* value ); + +ALAPI void ALAPIENTRY alGetListener3f( ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3 ); + +ALAPI void ALAPIENTRY alGetListenerfv( ALenum param, ALfloat* values ); + +ALAPI void ALAPIENTRY alGetListeneri( ALenum param, ALint* value ); + +ALAPI void ALAPIENTRY alGetListener3i( ALenum param, ALint *value1, ALint *value2, ALint *value3 ); + +ALAPI void ALAPIENTRY alGetListeneriv( ALenum param, ALint* values ); + + +/** + * SOURCE + * Sources represent individual sound objects in 3D-space. + * Sources take the PCM data provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial arrangement etc. + * + * Properties include: - + * + * Gain AL_GAIN ALfloat + * Min Gain AL_MIN_GAIN ALfloat + * Max Gain AL_MAX_GAIN ALfloat + * Position AL_POSITION ALfloat[3] + * Velocity AL_VELOCITY ALfloat[3] + * Direction AL_DIRECTION ALfloat[3] + * Head Relative Mode AL_SOURCE_RELATIVE ALint (AL_TRUE or AL_FALSE) + * Reference Distance AL_REFERENCE_DISTANCE ALfloat + * Max Distance AL_MAX_DISTANCE ALfloat + * RollOff Factor AL_ROLLOFF_FACTOR ALfloat + * Inner Angle AL_CONE_INNER_ANGLE ALint or ALfloat + * Outer Angle AL_CONE_OUTER_ANGLE ALint or ALfloat + * Cone Outer Gain AL_CONE_OUTER_GAIN ALint or ALfloat + * Pitch AL_PITCH ALfloat + * Looping AL_LOOPING ALint (AL_TRUE or AL_FALSE) + * MS Offset AL_MSEC_OFFSET ALint or ALfloat + * Byte Offset AL_BYTE_OFFSET ALint or ALfloat + * Sample Offset AL_SAMPLE_OFFSET ALint or ALfloat + * Attached Buffer AL_BUFFER ALint + * State (Query only) AL_SOURCE_STATE ALint + * Buffers Queued (Query only) AL_BUFFERS_QUEUED ALint + * Buffers Processed (Query only) AL_BUFFERS_PROCESSED ALint + */ + +/* Create Source objects */ +ALAPI void ALAPIENTRY alGenSources( ALsizei n, ALuint* sources ); + +/* Delete Source objects */ +ALAPI void ALAPIENTRY alDeleteSources( ALsizei n, const ALuint* sources ); + +/* Verify a handle is a valid Source */ +ALAPI ALboolean ALAPIENTRY alIsSource( ALuint sid ); + +/* + * Set Source parameters + */ +ALAPI void ALAPIENTRY alSourcef( ALuint sid, ALenum param, ALfloat value ); + +ALAPI void ALAPIENTRY alSource3f( ALuint sid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); + +ALAPI void ALAPIENTRY alSourcefv( ALuint sid, ALenum param, const ALfloat* values ); + +ALAPI void ALAPIENTRY alSourcei( ALuint sid, ALenum param, ALint value ); + +ALAPI void ALAPIENTRY alSource3i( ALuint sid, ALenum param, ALint value1, ALint value2, ALint value3 ); + +ALAPI void ALAPIENTRY alSourceiv( ALuint sid, ALenum param, const ALint* values ); + +/* + * Get Source parameters + */ +ALAPI void ALAPIENTRY alGetSourcef( ALuint sid, ALenum param, ALfloat* value ); + +ALAPI void ALAPIENTRY alGetSource3f( ALuint sid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); + +ALAPI void ALAPIENTRY alGetSourcefv( ALuint sid, ALenum param, ALfloat* values ); + +ALAPI void ALAPIENTRY alGetSourcei( ALuint sid, ALenum param, ALint* value ); + +ALAPI void ALAPIENTRY alGetSource3i( ALuint sid, ALenum param, ALint* value1, ALint* value2, ALint* value3); + +ALAPI void ALAPIENTRY alGetSourceiv( ALuint sid, ALenum param, ALint* values ); + + +/* + * Source vector based playback calls + */ + +/* Play, replay, or resume (if paused) a list of Sources */ +ALAPI void ALAPIENTRY alSourcePlayv( ALsizei ns, const ALuint *sids ); + +/* Stop a list of Sources */ +ALAPI void ALAPIENTRY alSourceStopv( ALsizei ns, const ALuint *sids ); + +/* Rewind a list of Sources */ +ALAPI void ALAPIENTRY alSourceRewindv( ALsizei ns, const ALuint *sids ); + +/* Pause a list of Sources */ +ALAPI void ALAPIENTRY alSourcePausev( ALsizei ns, const ALuint *sids ); + +/* + * Source based playback calls + */ + +/* Play, replay, or resume a Source */ +ALAPI void ALAPIENTRY alSourcePlay( ALuint sid ); + +/* Stop a Source */ +ALAPI void ALAPIENTRY alSourceStop( ALuint sid ); + +/* Rewind a Source (set playback postiton to beginning) */ +ALAPI void ALAPIENTRY alSourceRewind( ALuint sid ); + +/* Pause a Source */ +ALAPI void ALAPIENTRY alSourcePause( ALuint sid ); + +/* + * Source Queuing + */ +ALAPI void ALAPIENTRY alSourceQueueBuffers( ALuint sid, ALsizei numEntries, const ALuint *bids ); + +ALAPI void ALAPIENTRY alSourceUnqueueBuffers( ALuint sid, ALsizei numEntries, ALuint *bids ); + + +/** + * BUFFER + * Buffer objects are storage space for sample data. + * Buffers are referred to by Sources. One Buffer can be used + * by multiple Sources. + * + * Properties include: - + * + * Frequency (Query only) AL_FREQUENCY ALint + * Size (Query only) AL_SIZE ALint + * Bits (Query only) AL_BITS ALint + * Channels (Query only) AL_CHANNELS ALint + */ + +/* Create Buffer objects */ +ALAPI void ALAPIENTRY alGenBuffers( ALsizei n, ALuint* buffers ); + +/* Delete Buffer objects */ +ALAPI void ALAPIENTRY alDeleteBuffers( ALsizei n, const ALuint* buffers ); + +/* Verify a handle is a valid Buffer */ +ALAPI ALboolean ALAPIENTRY alIsBuffer( ALuint bid ); + +/* Specify the data to be copied into a buffer */ +ALAPI void ALAPIENTRY alBufferData( ALuint bid, ALenum format, const ALvoid* data, ALsizei size, ALsizei freq ); + +/* + * Set Buffer parameters + */ +ALAPI void ALAPIENTRY alBufferf( ALuint bid, ALenum param, ALfloat value ); + +ALAPI void ALAPIENTRY alBuffer3f( ALuint bid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); + +ALAPI void ALAPIENTRY alBufferfv( ALuint bid, ALenum param, const ALfloat* values ); + +ALAPI void ALAPIENTRY alBufferi( ALuint bid, ALenum param, ALint value ); + +ALAPI void ALAPIENTRY alBuffer3i( ALuint bid, ALenum param, ALint value1, ALint value2, ALint value3 ); + +ALAPI void ALAPIENTRY alBufferiv( ALuint bid, ALenum param, const ALint* values ); + +/* + * Get Buffer parameters + */ +ALAPI void ALAPIENTRY alGetBufferf( ALuint bid, ALenum param, ALfloat* value ); + +ALAPI void ALAPIENTRY alGetBuffer3f( ALuint bid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); + +ALAPI void ALAPIENTRY alGetBufferfv( ALuint bid, ALenum param, ALfloat* values ); + +ALAPI void ALAPIENTRY alGetBufferi( ALuint bid, ALenum param, ALint* value ); + +ALAPI void ALAPIENTRY alGetBuffer3i( ALuint bid, ALenum param, ALint* value1, ALint* value2, ALint* value3); + +ALAPI void ALAPIENTRY alGetBufferiv( ALuint bid, ALenum param, ALint* values ); + + +/* + * Global Parameters + */ +ALAPI void ALAPIENTRY alDopplerFactor( ALfloat value ); + +ALAPI void ALAPIENTRY alDopplerVelocity( ALfloat value ); + +ALAPI void ALAPIENTRY alSpeedOfSound( ALfloat value ); + +ALAPI void ALAPIENTRY alDistanceModel( ALenum distanceModel ); + +#else /* AL_NO_PROTOTYPES */ + +/* +void (ALAPIENTRY *alEnable)( ALenum capability ); +void (ALAPIENTRY *alDisable)( ALenum capability ); +ALboolean (ALAPIENTRY *alIsEnabled)( ALenum capability ); +const ALchar* (ALAPIENTRY *alGetString)( ALenum param ); +void (ALAPIENTRY *alGetBooleanv)( ALenum param, ALboolean* data ); +void (ALAPIENTRY *alGetIntegerv)( ALenum param, ALint* data ); +void (ALAPIENTRY *alGetFloatv)( ALenum param, ALfloat* data ); +void (ALAPIENTRY *alGetDoublev)( ALenum param, ALdouble* data ); +ALboolean (ALAPIENTRY *alGetBoolean)( ALenum param ); +ALint (ALAPIENTRY *alGetInteger)( ALenum param ); +ALfloat (ALAPIENTRY *alGetFloat)( ALenum param ); +ALdouble (ALAPIENTRY *alGetDouble)( ALenum param ); +ALenum (ALAPIENTRY *alGetError)( ALvoid ); +ALboolean (ALAPIENTRY *alIsExtensionPresent)(const ALchar* extname ); +void* (ALAPIENTRY *alGetProcAddress)( const ALchar* fname ); +ALenum (ALAPIENTRY *alGetEnumValue)( const ALchar* ename ); +void (ALAPIENTRY *alListenerf)( ALenum param, ALfloat value ); +void (ALAPIENTRY *alListener3f)( ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +void (ALAPIENTRY *alListenerfv)( ALenum param, const ALfloat* values ); +void (ALAPIENTRY *alListeneri)( ALenum param, ALint value ); +void (ALAPIENTRY *alListener3i)( ALenum param, ALint value1, ALint value2, ALint value3 ); +void (ALAPIENTRY *alListeneriv)( ALenum param, const ALint* values ); +void (ALAPIENTRY *alGetListenerf)( ALenum param, ALfloat* value ); +void (ALAPIENTRY *alGetListener3f)( ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3 ); +void (ALAPIENTRY *alGetListenerfv)( ALenum param, ALfloat* values ); +void (ALAPIENTRY *alGetListeneri)( ALenum param, ALint* value ); +void (ALAPIENTRY *alGetListener3i)( ALenum param, ALint *value1, ALint *value2, ALint *value3 ); +void (ALAPIENTRY *alGetListeneriv)( ALenum param, ALint* values ); +void (ALAPIENTRY *alGenSources)( ALsizei n, ALuint* sources ); +void (ALAPIENTRY *alDeleteSources)( ALsizei n, const ALuint* sources ); +ALboolean (ALAPIENTRY *alIsSource)( ALuint sid ); +void (ALAPIENTRY *alSourcef)( ALuint sid, ALenum param, ALfloat value); +void (ALAPIENTRY *alSource3f)( ALuint sid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +void (ALAPIENTRY *alSourcefv)( ALuint sid, ALenum param, const ALfloat* values ); +void (ALAPIENTRY *alSourcei)( ALuint sid, ALenum param, ALint value); +void (ALAPIENTRY *alSource3i)( ALuint sid, ALenum param, ALint value1, ALint value2, ALint value3 ); +void (ALAPIENTRY *alSourceiv)( ALuint sid, ALenum param, const ALint* values ); +void (ALAPIENTRY *alGetSourcef)( ALuint sid, ALenum param, ALfloat* value ); +void (ALAPIENTRY *alGetSource3f)( ALuint sid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); +void (ALAPIENTRY *alGetSourcefv)( ALuint sid, ALenum param, ALfloat* values ); +void (ALAPIENTRY *alGetSourcei)( ALuint sid, ALenum param, ALint* value ); +void (ALAPIENTRY *alGetSource3i)( ALuint sid, ALenum param, ALint* value1, ALint* value2, ALint* value3); +void (ALAPIENTRY *alGetSourceiv)( ALuint sid, ALenum param, ALint* values ); +void (ALAPIENTRY *alSourcePlayv)( ALsizei ns, const ALuint *sids ); +void (ALAPIENTRY *alSourceStopv)( ALsizei ns, const ALuint *sids ); +void (ALAPIENTRY *alSourceRewindv)( ALsizei ns, const ALuint *sids ); +void (ALAPIENTRY *alSourcePausev)( ALsizei ns, const ALuint *sids ); +void (ALAPIENTRY *alSourcePlay)( ALuint sid ); +void (ALAPIENTRY *alSourceStop)( ALuint sid ); +void (ALAPIENTRY *alSourceRewind)( ALuint sid ); +void (ALAPIENTRY *alSourcePause)( ALuint sid ); +void (ALAPIENTRY *alSourceQueueBuffers)( ALuint sid, ALsizei numEntries, const ALuint *bids ); +void (ALAPIENTRY *alSourceUnqueueBuffers)( ALuint sid, ALsizei numEntries, ALuint *bids ); +void (ALAPIENTRY *alGenBuffers)( ALsizei n, ALuint* buffers ); +void (ALAPIENTRY *alDeleteBuffers)( ALsizei n, const ALuint* buffers ); +ALboolean (ALAPIENTRY *alIsBuffer)( ALuint bid ); +void (ALAPIENTRY *alBufferData)( ALuint bid, ALenum format, const ALvoid* data, ALsizei size, ALsizei freq ); +void (ALAPIENTRY *alBufferf)( ALuint bid, ALenum param, ALfloat value); +void (ALAPIENTRY *alBuffer3f)( ALuint bid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +void (ALAPIENTRY *alBufferfv)( ALuint bid, ALenum param, const ALfloat* values ); +void (ALAPIENTRY *alBufferi)( ALuint bid, ALenum param, ALint value); +void (ALAPIENTRY *alBuffer3i)( ALuint bid, ALenum param, ALint value1, ALint value2, ALint value3 ); +void (ALAPIENTRY *alBufferiv)( ALuint bid, ALenum param, const ALint* values ); +void (ALAPIENTRY *alGetBufferf)( ALuint bid, ALenum param, ALfloat* value ); +void (ALAPIENTRY *alGetBuffer3f)( ALuint bid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); +void (ALAPIENTRY *alGetBufferfv)( ALuint bid, ALenum param, ALfloat* values ); +void (ALAPIENTRY *alGetBufferi)( ALuint bid, ALenum param, ALint* value ); +void (ALAPIENTRY *alGetBuffer3i)( ALuint bid, ALenum param, ALint* value1, ALint* value2, ALint* value3); +void (ALAPIENTRY *alGetBufferiv)( ALuint bid, ALenum param, ALint* values ); +void (ALAPIENTRY *alDopplerFactor)( ALfloat value ); +void (ALAPIENTRY *alDopplerVelocity)( ALfloat value ); +void (ALAPIENTRY *alSpeedOfSound)( ALfloat value ); +void (ALAPIENTRY *alDistanceModel)( ALenum distanceModel ); +*/ +/* Type Definitions */ + +typedef void (ALAPIENTRY *LPALENABLE)( ALenum capability ); +typedef void (ALAPIENTRY *LPALDISABLE)( ALenum capability ); +typedef ALboolean (ALAPIENTRY *LPALISENABLED)( ALenum capability ); +typedef const ALchar* (ALAPIENTRY *LPALGETSTRING)( ALenum param ); +typedef void (ALAPIENTRY *LPALGETBOOLEANV)( ALenum param, ALboolean* data ); +typedef void (ALAPIENTRY *LPALGETINTEGERV)( ALenum param, ALint* data ); +typedef void (ALAPIENTRY *LPALGETFLOATV)( ALenum param, ALfloat* data ); +typedef void (ALAPIENTRY *LPALGETDOUBLEV)( ALenum param, ALdouble* data ); +typedef ALboolean (ALAPIENTRY *LPALGETBOOLEAN)( ALenum param ); +typedef ALint (ALAPIENTRY *LPALGETINTEGER)( ALenum param ); +typedef ALfloat (ALAPIENTRY *LPALGETFLOAT)( ALenum param ); +typedef ALdouble (ALAPIENTRY *LPALGETDOUBLE)( ALenum param ); +typedef ALenum (ALAPIENTRY *LPALGETERROR)( ALvoid ); +typedef ALboolean (ALAPIENTRY *LPALISEXTENSIONPRESENT)(const ALchar* extname ); +typedef void* (ALAPIENTRY *LPALGETPROCADDRESS)( const ALchar* fname ); +typedef ALenum (ALAPIENTRY *LPALGETENUMVALUE)( const ALchar* ename ); +typedef void (ALAPIENTRY *LPALLISTENERF)( ALenum param, ALfloat value ); +typedef void (ALAPIENTRY *LPALLISTENER3F)( ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +typedef void (ALAPIENTRY *LPALLISTENERFV)( ALenum param, const ALfloat* values ); +typedef void (ALAPIENTRY *LPALLISTENERI)( ALenum param, ALint value ); +typedef void (ALAPIENTRY *LPALLISTENER3I)( ALenum param, ALint value1, ALint value2, ALint value3 ); +typedef void (ALAPIENTRY *LPALLISTENERIV)( ALenum param, const ALint* values ); +typedef void (ALAPIENTRY *LPALGETLISTENERF)( ALenum param, ALfloat* value ); +typedef void (ALAPIENTRY *LPALGETLISTENER3F)( ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3 ); +typedef void (ALAPIENTRY *LPALGETLISTENERFV)( ALenum param, ALfloat* values ); +typedef void (ALAPIENTRY *LPALGETLISTENERI)( ALenum param, ALint* value ); +typedef void (ALAPIENTRY *LPALGETLISTENER3I)( ALenum param, ALint *value1, ALint *value2, ALint *value3 ); +typedef void (ALAPIENTRY *LPALGETLISTENERIV)( ALenum param, ALint* values ); +typedef void (ALAPIENTRY *LPALGENSOURCES)( ALsizei n, ALuint* sources ); +typedef void (ALAPIENTRY *LPALDELETESOURCES)( ALsizei n, const ALuint* sources ); +typedef ALboolean (ALAPIENTRY *LPALISSOURCE)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEF)( ALuint sid, ALenum param, ALfloat value); +typedef void (ALAPIENTRY *LPALSOURCE3F)( ALuint sid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +typedef void (ALAPIENTRY *LPALSOURCEFV)( ALuint sid, ALenum param, const ALfloat* values ); +typedef void (ALAPIENTRY *LPALSOURCEI)( ALuint sid, ALenum param, ALint value); +typedef void (ALAPIENTRY *LPALSOURCE3I)( ALuint sid, ALenum param, ALint value1, ALint value2, ALint value3 ); +typedef void (ALAPIENTRY *LPALSOURCEIV)( ALuint sid, ALenum param, const ALint* values ); +typedef void (ALAPIENTRY *LPALGETSOURCEF)( ALuint sid, ALenum param, ALfloat* value ); +typedef void (ALAPIENTRY *LPALGETSOURCE3F)( ALuint sid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); +typedef void (ALAPIENTRY *LPALGETSOURCEFV)( ALuint sid, ALenum param, ALfloat* values ); +typedef void (ALAPIENTRY *LPALGETSOURCEI)( ALuint sid, ALenum param, ALint* value ); +typedef void (ALAPIENTRY *LPALGETSOURCE3I)( ALuint sid, ALenum param, ALint* value1, ALint* value2, ALint* value3); +typedef void (ALAPIENTRY *LPALGETSOURCEIV)( ALuint sid, ALenum param, ALint* values ); +typedef void (ALAPIENTRY *LPALSOURCEPLAYV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCESTOPV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCEREWINDV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCEPAUSEV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCEPLAY)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCESTOP)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEREWIND)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEPAUSE)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint sid, ALsizei numEntries, const ALuint *bids ); +typedef void (ALAPIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint sid, ALsizei numEntries, ALuint *bids ); +typedef void (ALAPIENTRY *LPALGENBUFFERS)( ALsizei n, ALuint* buffers ); +typedef void (ALAPIENTRY *LPALDELETEBUFFERS)( ALsizei n, const ALuint* buffers ); +typedef ALboolean (ALAPIENTRY *LPALISBUFFER)( ALuint bid ); +typedef void (ALAPIENTRY *LPALBUFFERDATA)( ALuint bid, ALenum format, const ALvoid* data, ALsizei size, ALsizei freq ); +typedef void (ALAPIENTRY *LPALBUFFERF)( ALuint bid, ALenum param, ALfloat value); +typedef void (ALAPIENTRY *LPALBUFFER3F)( ALuint bid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +typedef void (ALAPIENTRY *LPALBUFFERFV)( ALuint bid, ALenum param, const ALfloat* values ); +typedef void (ALAPIENTRY *LPALBUFFERI)( ALuint bid, ALenum param, ALint value); +typedef void (ALAPIENTRY *LPALBUFFER3I)( ALuint bid, ALenum param, ALint value1, ALint value2, ALint value3 ); +typedef void (ALAPIENTRY *LPALBUFFERIV)( ALuint bid, ALenum param, const ALint* values ); +typedef void (ALAPIENTRY *LPALGETBUFFERF)( ALuint bid, ALenum param, ALfloat* value ); +typedef void (ALAPIENTRY *LPALGETBUFFER3F)( ALuint bid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); +typedef void (ALAPIENTRY *LPALGETBUFFERFV)( ALuint bid, ALenum param, ALfloat* values ); +typedef void (ALAPIENTRY *LPALGETBUFFERI)( ALuint bid, ALenum param, ALint* value ); +typedef void (ALAPIENTRY *LPALGETBUFFER3I)( ALuint bid, ALenum param, ALint* value1, ALint* value2, ALint* value3); +typedef void (ALAPIENTRY *LPALGETBUFFERIV)( ALuint bid, ALenum param, ALint* values ); +typedef void (ALAPIENTRY *LPALDOPPLERFACTOR)( ALfloat value ); +typedef void (ALAPIENTRY *LPALDOPPLERVELOCITY)( ALfloat value ); +typedef void (ALAPIENTRY *LPALSPEEDOFSOUND)( ALfloat value ); +typedef void (ALAPIENTRY *LPALDISTANCEMODEL)( ALenum distanceModel ); + +#endif /* AL_NO_PROTOTYPES */ + +#ifdef TARGET_OS_MAC +#if TARGET_OS_MAC +#pragma export off +#endif /* TARGET_OS_MAC */ +#endif /* TARGET_OS_MAC */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __al_h_ */ diff --git a/reaction/engine/code/AL/alc.h b/reaction/engine/code/AL/alc.h new file mode 100644 index 00000000..f3a41bce --- /dev/null +++ b/reaction/engine/code/AL/alc.h @@ -0,0 +1,166 @@ +#ifndef ALC_CONTEXT_H_ +#define ALC_CONTEXT_H_ + +#include "altypes.h" +#include "alctypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ALC_VERSION_0_1 1 + +#ifdef _WIN32 + typedef struct ALCdevice_struct ALCdevice; + typedef struct ALCcontext_struct ALCcontext; + #ifndef _XBOX + #ifdef _OPENAL32LIB + #define ALCAPI __declspec(dllexport) + #else + #define ALCAPI __declspec(dllimport) + #endif + #define ALCAPIENTRY __cdecl + #endif +#endif + +#ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export on + #endif +#endif + +#ifndef ALCAPI + #define ALCAPI +#endif + +#ifndef ALCAPIENTRY + #define ALCAPIENTRY +#endif + + +#ifndef ALC_NO_PROTOTYPES + +/* + * Context Management + */ +ALCAPI ALCcontext * ALCAPIENTRY alcCreateContext( ALCdevice *device, const ALCint* attrlist ); + +ALCAPI ALCboolean ALCAPIENTRY alcMakeContextCurrent( ALCcontext *context ); + +ALCAPI void ALCAPIENTRY alcProcessContext( ALCcontext *context ); + +ALCAPI void ALCAPIENTRY alcSuspendContext( ALCcontext *context ); + +ALCAPI void ALCAPIENTRY alcDestroyContext( ALCcontext *context ); + +ALCAPI ALCcontext * ALCAPIENTRY alcGetCurrentContext( ALCvoid ); + +ALCAPI ALCdevice* ALCAPIENTRY alcGetContextsDevice( ALCcontext *context ); + + +/* + * Device Management + */ +ALCAPI ALCdevice * ALCAPIENTRY alcOpenDevice( const ALchar *devicename ); + +ALCAPI ALCboolean ALCAPIENTRY alcCloseDevice( ALCdevice *device ); + + +/* + * Error support. + * Obtain the most recent Context error + */ +ALCAPI ALCenum ALCAPIENTRY alcGetError( ALCdevice *device ); + + +/* + * Extension support. + * Query for the presence of an extension, and obtain any appropriate + * function pointers and enum values. + */ +ALCAPI ALCboolean ALCAPIENTRY alcIsExtensionPresent( ALCdevice *device, const ALCchar *extname ); + +ALCAPI void * ALCAPIENTRY alcGetProcAddress( ALCdevice *device, const ALCchar *funcname ); + +ALCAPI ALCenum ALCAPIENTRY alcGetEnumValue( ALCdevice *device, const ALCchar *enumname ); + + +/* + * Query functions + */ +ALCAPI const ALCchar * ALCAPIENTRY alcGetString( ALCdevice *device, ALCenum param ); + +ALCAPI void ALCAPIENTRY alcGetIntegerv( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *data ); + + +/* + * Capture functions + */ +ALCAPI ALCdevice* ALCAPIENTRY alcCaptureOpenDevice( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ); + +ALCAPI ALCboolean ALCAPIENTRY alcCaptureCloseDevice( ALCdevice *device ); + +ALCAPI void ALCAPIENTRY alcCaptureStart( ALCdevice *device ); + +ALCAPI void ALCAPIENTRY alcCaptureStop( ALCdevice *device ); + +ALCAPI void ALCAPIENTRY alcCaptureSamples( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); + +#else /* ALC_NO_PROTOTYPES */ +/* +ALCAPI ALCcontext * (ALCAPIENTRY *alcCreateContext)( ALCdevice *device, const ALCint* attrlist ); +ALCAPI ALCboolean (ALCAPIENTRY *alcMakeContextCurrent)( ALCcontext *context ); +ALCAPI void (ALCAPIENTRY *alcProcessContext)( ALCcontext *context ); +ALCAPI void (ALCAPIENTRY *alcSuspendContext)( ALCcontext *context ); +ALCAPI void (ALCAPIENTRY *alcDestroyContext)( ALCcontext *context ); +ALCAPI ALCcontext * (ALCAPIENTRY *alcGetCurrentContext)( ALCvoid ); +ALCAPI ALCdevice * (ALCAPIENTRY *alcGetContextsDevice)( ALCcontext *context ); +ALCAPI ALCdevice * (ALCAPIENTRY *alcOpenDevice)( const ALCchar *devicename ); +ALCAPI ALCboolean (ALCAPIENTRY *alcCloseDevice)( ALCdevice *device ); +ALCAPI ALCenum (ALCAPIENTRY *alcGetError)( ALCdevice *device ); +ALCAPI ALCboolean (ALCAPIENTRY *alcIsExtensionPresent)( ALCdevice *device, const ALCchar *extname ); +ALCAPI void * (ALCAPIENTRY *alcGetProcAddress)( ALCdevice *device, const ALCchar *funcname ); +ALCAPI ALCenum (ALCAPIENTRY *alcGetEnumValue)( ALCdevice *device, const ALCchar *enumname ); +ALCAPI const ALCchar* (ALCAPIENTRY *alcGetString)( ALCdevice *device, ALCenum param ); +ALCAPI void (ALCAPIENTRY *alcGetIntegerv)( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *dest ); +ALCAPI ALCdevice * (ALCAPIENTRY *alcCaptureOpenDevice)( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ); +ALCAPI ALCboolean (ALCAPIENTRY *alcCaptureCloseDevice)( ALCdevice *device ); +ALCAPI void (ALCAPIENTRY *alcCaptureStart)( ALCdevice *device ); +ALCAPI void (ALCAPIENTRY *alcCaptureStop)( ALCdevice *device ); +ALCAPI void (ALCAPIENTRY *alcCaptureSamples)( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); +*/ +/* Type definitions */ +typedef ALCcontext * (ALCAPIENTRY *LPALCCREATECONTEXT) (ALCdevice *device, const ALCint *attrlist); +typedef ALCboolean (ALCAPIENTRY *LPALCMAKECONTEXTCURRENT)( ALCcontext *context ); +typedef void (ALCAPIENTRY *LPALCPROCESSCONTEXT)( ALCcontext *context ); +typedef void (ALCAPIENTRY *LPALCSUSPENDCONTEXT)( ALCcontext *context ); +typedef void (ALCAPIENTRY *LPALCDESTROYCONTEXT)( ALCcontext *context ); +typedef ALCcontext * (ALCAPIENTRY *LPALCGETCURRENTCONTEXT)( ALCvoid ); +typedef ALCdevice * (ALCAPIENTRY *LPALCGETCONTEXTSDEVICE)( ALCcontext *context ); +typedef ALCdevice * (ALCAPIENTRY *LPALCOPENDEVICE)( const ALCchar *devicename ); +typedef ALCboolean (ALCAPIENTRY *LPALCCLOSEDEVICE)( ALCdevice *device ); +typedef ALCenum (ALCAPIENTRY *LPALCGETERROR)( ALCdevice *device ); +typedef ALCboolean (ALCAPIENTRY *LPALCISEXTENSIONPRESENT)( ALCdevice *device, const ALCchar *extname ); +typedef void * (ALCAPIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname ); +typedef ALCenum (ALCAPIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname ); +typedef const ALCchar* (ALCAPIENTRY *LPALCGETSTRING)( ALCdevice *device, ALCenum param ); +typedef void (ALCAPIENTRY *LPALCGETINTEGERV)( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *dest ); +typedef ALCdevice * (ALCAPIENTRY *LPALCCAPTUREOPENDEVICE)( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ); +typedef ALCboolean (ALCAPIENTRY *LPALCCAPTURECLOSEDEVICE)( ALCdevice *device ); +typedef void (ALCAPIENTRY *LPALCCAPTURESTART)( ALCdevice *device ); +typedef void (ALCAPIENTRY *LPALCCAPTURESTOP)( ALCdevice *device ); +typedef void (ALCAPIENTRY *LPALCCAPTURESAMPLES)( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); + +#endif /* ALC_NO_PROTOTYPES */ + +#ifdef TARGET_OS_MAC +#if TARGET_OS_MAC +#pragma export off +#endif /* TARGET_OS_MAC */ +#endif /* TARGET_OS_MAC */ + +#ifdef __cplusplus +} +#endif + +#endif /* ALC_CONTEXT_H_ */ diff --git a/reaction/engine/code/AL/alctypes.h b/reaction/engine/code/AL/alctypes.h new file mode 100644 index 00000000..181384b0 --- /dev/null +++ b/reaction/engine/code/AL/alctypes.h @@ -0,0 +1,142 @@ +#ifndef _ALCTYPES_H_ +#define _ALCTYPES_H_ + +#if !defined(_WIN32) +struct _AL_device; +typedef struct _AL_device ALCdevice; + +typedef void ALCcontext; +#endif /* _WIN32 */ + +typedef int ALCenum; + +/** ALC boolean type. */ +typedef char ALCboolean; + +/** ALC 8bit signed byte. */ +typedef char ALCbyte; + +/** ALC 8bit unsigned byte. */ +typedef unsigned char ALCubyte; + +/** OpenAL 8bit char */ +typedef char ALCchar; + +/** ALC 16bit signed short integer type. */ +typedef short ALCshort; + +/** ALC 16bit unsigned short integer type. */ +typedef unsigned short ALCushort; + +/** ALC 32bit unsigned integer type. */ +typedef unsigned ALCuint; + +/** ALC 32bit signed integer type. */ +typedef int ALCint; + +/** ALC 32bit floating point type. */ +typedef float ALCfloat; + +/** ALC 64bit double point type. */ +typedef double ALCdouble; + +/** ALC 32bit type. */ +typedef int ALCsizei; + +/** ALC void type */ +typedef void ALCvoid; + +/* Enumerant values begin at column 50. No tabs. */ + +/* bad value */ +#define ALC_INVALID 0 + +/* Boolean False. */ +#define ALC_FALSE 0 + +/* Boolean True. */ +#define ALC_TRUE 1 + +/** + * followed by Hz + */ +#define ALC_FREQUENCY 0x1007 + +/** + * followed by Hz + */ +#define ALC_REFRESH 0x1008 + +/** + * followed by AL_TRUE, AL_FALSE + */ +#define ALC_SYNC 0x1009 + +/** + * followed by Num of requested Mono (3D) Sources + */ +#define ALC_MONO_SOURCES 0x1010 + +/** + * followed by Num of requested Stereo Sources + */ +#define ALC_STEREO_SOURCES 0x1011 + +/** + * errors + */ + +/** + * No error + */ +#define ALC_NO_ERROR ALC_FALSE + +/** + * No device + */ +#define ALC_INVALID_DEVICE 0xA001 + +/** + * invalid context ID + */ +#define ALC_INVALID_CONTEXT 0xA002 + +/** + * bad enum + */ +#define ALC_INVALID_ENUM 0xA003 + +/** + * bad value + */ +#define ALC_INVALID_VALUE 0xA004 + +/** + * Out of memory. + */ +#define ALC_OUT_OF_MEMORY 0xA005 + + + +/** + * The Specifier string for default device + */ +#define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004 +#define ALC_DEVICE_SPECIFIER 0x1005 +#define ALC_EXTENSIONS 0x1006 + +#define ALC_MAJOR_VERSION 0x1000 +#define ALC_MINOR_VERSION 0x1001 + +#define ALC_ATTRIBUTES_SIZE 0x1002 +#define ALC_ALL_ATTRIBUTES 0x1003 + +/** + * Capture extension + */ +#define ALC_CAPTURE_DEVICE_SPECIFIER 0x310 +#define ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER 0x311 +#define ALC_CAPTURE_SAMPLES 0x312 + + +#endif /* _ALCTYPES_H */ diff --git a/reaction/engine/code/AL/altypes.h b/reaction/engine/code/AL/altypes.h new file mode 100644 index 00000000..3ec959fd --- /dev/null +++ b/reaction/engine/code/AL/altypes.h @@ -0,0 +1,352 @@ +#ifndef _AL_TYPES_H_ +#define _AL_TYPES_H_ + +/* define platform type */ +#if !defined(MACINTOSH_AL) && !defined(LINUX_AL) && !defined(WINDOWS_AL) + #ifdef __APPLE__ + #define MACINTOSH_AL + #else + #ifdef _WIN32 + #define WINDOWS_AL + #else + #define LINUX_AL + #endif + #endif +#endif + +/** OpenAL bool type. */ +typedef char ALboolean; + +/** OpenAL 8bit signed byte. */ +typedef char ALbyte; + +/** OpenAL 8bit unsigned byte. */ +typedef unsigned char ALubyte; + +/** OpenAL 8bit char */ +typedef char ALchar; + +/** OpenAL 16bit signed short integer type. */ +typedef short ALshort; + +/** OpenAL 16bit unsigned short integer type. */ +typedef unsigned short ALushort; + +/** OpenAL 32bit unsigned integer type. */ +typedef unsigned int ALuint; + +/** OpenAL 32bit signed integer type. */ +typedef int ALint; + +/** OpenAL 32bit floating point type. */ +typedef float ALfloat; + +/** OpenAL 64bit double point type. */ +typedef double ALdouble; + +/** OpenAL 32bit type. */ +typedef int ALsizei; + +/** OpenAL void type (for params, not returns). */ +typedef void ALvoid; + +/** OpenAL enumerations. */ +typedef int ALenum; + +/** OpenAL bitfields. */ +typedef unsigned int ALbitfield; + +/** OpenAL clamped float. */ +typedef ALfloat ALclampf; + +/** Openal clamped double. */ +typedef ALdouble ALclampd; + +/* Enumerant values begin at column 50. No tabs. */ + +/* bad value */ +#define AL_INVALID -1 + +#define AL_NONE 0 + +/* Boolean False. */ +#define AL_FALSE 0 + +/** Boolean True. */ +#define AL_TRUE 1 + +/** Indicate Source has relative coordinates. */ +#define AL_SOURCE_RELATIVE 0x202 + + + +/** + * Directional source, inner cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_INNER_ANGLE 0x1001 + +/** + * Directional source, outer cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_OUTER_ANGLE 0x1002 + +/** + * Specify the pitch to be applied, either at source, + * or on mixer results, at listener. + * Range: [0.5-2.0] + * Default: 1.0 + */ +#define AL_PITCH 0x1003 + +/** + * Specify the current location in three dimensional space. + * OpenAL, like OpenGL, uses a right handed coordinate system, + * where in a frontal default view X (thumb) points right, + * Y points up (index finger), and Z points towards the + * viewer/camera (middle finger). + * To switch from a left handed coordinate system, flip the + * sign on the Z coordinate. + * Listener position is always in the world coordinate system. + */ +#define AL_POSITION 0x1004 + +/** Specify the current direction. */ +#define AL_DIRECTION 0x1005 + +/** Specify the current velocity in three dimensional space. */ +#define AL_VELOCITY 0x1006 + +/** + * Indicate whether source is looping. + * Type: ALboolean? + * Range: [AL_TRUE, AL_FALSE] + * Default: FALSE. + */ +#define AL_LOOPING 0x1007 + +/** + * Indicate the buffer to provide sound samples. + * Type: ALuint. + * Range: any valid Buffer id. + */ +#define AL_BUFFER 0x1009 + +/** + * Indicate the gain (volume amplification) applied. + * Type: ALfloat. + * Range: ]0.0- ] + * A value of 1.0 means un-attenuated/unchanged. + * Each division by 2 equals an attenuation of -6dB. + * Each multiplicaton with 2 equals an amplification of +6dB. + * A value of 0.0 is meaningless with respect to a logarithmic + * scale; it is interpreted as zero volume - the channel + * is effectively disabled. + */ +#define AL_GAIN 0x100A + +/* + * Indicate minimum source attenuation + * Type: ALfloat + * Range: [0.0 - 1.0] + * + * Logarthmic + */ +#define AL_MIN_GAIN 0x100D + +/** + * Indicate maximum source attenuation + * Type: ALfloat + * Range: [0.0 - 1.0] + * + * Logarthmic + */ +#define AL_MAX_GAIN 0x100E + +/** + * Indicate listener orientation. + * + * at/up + */ +#define AL_ORIENTATION 0x100F + +/** + * Specify the channel mask. (Creative) + * Type: ALuint + * Range: [0 - 255] + */ +#define AL_CHANNEL_MASK 0x3000 + + +/** + * Source state information. + */ +#define AL_SOURCE_STATE 0x1010 +#define AL_INITIAL 0x1011 +#define AL_PLAYING 0x1012 +#define AL_PAUSED 0x1013 +#define AL_STOPPED 0x1014 + +/** + * Buffer Queue params + */ +#define AL_BUFFERS_QUEUED 0x1015 +#define AL_BUFFERS_PROCESSED 0x1016 + +/** + * Source buffer position information + */ +#define AL_SEC_OFFSET 0x1024 +#define AL_SAMPLE_OFFSET 0x1025 +#define AL_BYTE_OFFSET 0x1026 + +/* + * Source type (Static, Streaming or undetermined) + * Source is Static if a Buffer has been attached using AL_BUFFER + * Source is Streaming if one or more Buffers have been attached using alSourceQueueBuffers + * Source is undetermined when it has the NULL buffer attached + */ +#define AL_SOURCE_TYPE 0x1027 +#define AL_STATIC 0x1028 +#define AL_STREAMING 0x1029 +#define AL_UNDETERMINED 0x1030 + +/** Sound samples: format specifier. */ +#define AL_FORMAT_MONO8 0x1100 +#define AL_FORMAT_MONO16 0x1101 +#define AL_FORMAT_STEREO8 0x1102 +#define AL_FORMAT_STEREO16 0x1103 + +/** + * source specific reference distance + * Type: ALfloat + * Range: 0.0 - +inf + * + * At 0.0, no distance attenuation occurs. Default is + * 1.0. + */ +#define AL_REFERENCE_DISTANCE 0x1020 + +/** + * source specific rolloff factor + * Type: ALfloat + * Range: 0.0 - +inf + * + */ +#define AL_ROLLOFF_FACTOR 0x1021 + +/** + * Directional source, outer cone gain. + * + * Default: 0.0 + * Range: [0.0 - 1.0] + * Logarithmic + */ +#define AL_CONE_OUTER_GAIN 0x1022 + +/** + * Indicate distance above which sources are not + * attenuated using the inverse clamped distance model. + * + * Default: +inf + * Type: ALfloat + * Range: 0.0 - +inf + */ +#define AL_MAX_DISTANCE 0x1023 + +/** + * Sound samples: frequency, in units of Hertz [Hz]. + * This is the number of samples per second. Half of the + * sample frequency marks the maximum significant + * frequency component. + */ +#define AL_FREQUENCY 0x2001 +#define AL_BITS 0x2002 +#define AL_CHANNELS 0x2003 +#define AL_SIZE 0x2004 +#define AL_DATA 0x2005 + +/** + * Buffer state. + * + * Not supported for public use (yet). + */ +#define AL_UNUSED 0x2010 +#define AL_PENDING 0x2011 +#define AL_PROCESSED 0x2012 + + +/** Errors: No Error. */ +#define AL_NO_ERROR AL_FALSE + +/** + * Invalid Name paramater passed to AL call. + */ +#define AL_INVALID_NAME 0xA001 + +/** + * Invalid parameter passed to AL call. + */ +#define AL_ILLEGAL_ENUM 0xA002 +#define AL_INVALID_ENUM 0xA002 + +/** + * Invalid enum parameter value. + */ +#define AL_INVALID_VALUE 0xA003 + +/** + * Illegal call. + */ +#define AL_ILLEGAL_COMMAND 0xA004 +#define AL_INVALID_OPERATION 0xA004 + + +/** + * No mojo. + */ +#define AL_OUT_OF_MEMORY 0xA005 + + +/** Context strings: Vendor Name. */ +#define AL_VENDOR 0xB001 +#define AL_VERSION 0xB002 +#define AL_RENDERER 0xB003 +#define AL_EXTENSIONS 0xB004 + +/** Global tweakage. */ + +/** + * Doppler scale. Default 1.0 + */ +#define AL_DOPPLER_FACTOR 0xC000 + +/** + * Tweaks speed of propagation. + */ +#define AL_DOPPLER_VELOCITY 0xC001 + +/** + * Speed of Sound in units per second + */ +#define AL_SPEED_OF_SOUND 0xC003 + +/** + * Distance models + * + * used in conjunction with DistanceModel + * + * implicit: NONE, which disances distance attenuation. + */ +#define AL_DISTANCE_MODEL 0xD000 +#define AL_INVERSE_DISTANCE 0xD001 +#define AL_INVERSE_DISTANCE_CLAMPED 0xD002 +#define AL_LINEAR_DISTANCE 0xD003 +#define AL_LINEAR_DISTANCE_CLAMPED 0xD004 +#define AL_EXPONENT_DISTANCE 0xD005 +#define AL_EXPONENT_DISTANCE_CLAMPED 0xD006 + +#endif diff --git a/reaction/engine/code/AL/alut.h b/reaction/engine/code/AL/alut.h new file mode 100644 index 00000000..e29ae582 --- /dev/null +++ b/reaction/engine/code/AL/alut.h @@ -0,0 +1,90 @@ +#ifndef _ALUT_H_ +#define _ALUT_H_ + +/* define platform type */ +#if !defined(MACINTOSH_AL) && !defined(LINUX_AL) && !defined(WINDOWS_AL) + #ifdef __APPLE__ + #define MACINTOSH_AL + #else + #ifdef _WIN32 + #define WINDOWS_AL + #else + #define LINUX_AL + #endif + #endif +#endif + +#include "altypes.h" + +#ifdef _WIN32 +#define ALUTAPI +#define ALUTAPIENTRY __cdecl +#define AL_CALLBACK +#else /* _WIN32 */ + +#ifdef TARGET_OS_MAC +#if TARGET_OS_MAC +#pragma export on +#endif /* TARGET_OS_MAC */ +#endif /* TARGET_OS_MAC */ + +#ifndef ALUTAPI +#define ALUTAPI +#endif + +#ifndef ALUTAPIENTRY +#define ALUTAPIENTRY +#endif + +#ifndef AL_CALLBACK +#define AL_CALLBACK +#endif + +#endif /* _WIN32 */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ALUT_NO_PROTOTYPES + +ALUTAPI void ALUTAPIENTRY alutInit(int *argc, char *argv[]); +ALUTAPI void ALUTAPIENTRY alutExit(ALvoid); + +#ifndef MACINTOSH_AL +/* Windows and Linux versions have a loop parameter, Macintosh doesn't */ +ALUTAPI void ALUTAPIENTRY alutLoadWAVFile(ALbyte *file, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *freq, ALboolean *loop); +ALUTAPI void ALUTAPIENTRY alutLoadWAVMemory(ALbyte *memory, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *freq, ALboolean *loop); +#else +ALUTAPI void ALUTAPIENTRY alutLoadWAVFile(ALbyte *file, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *freq); +ALUTAPI void ALUTAPIENTRY alutLoadWAVMemory(ALbyte *memory, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *freq); +#endif + +ALUTAPI void ALUTAPIENTRY alutUnloadWAV(ALenum format, ALvoid *data, ALsizei size, ALsizei freq); + +#else /* ALUT_NO_PROTOTYPES */ + + void (ALUTAPIENTRY *alutInit)( int *argc, char *argv[] ); + void (ALUTAPIENTRY *alutExit)( ALvoid ); +#ifndef MACINTOSH_AL + void (ALUTAPIENTRY *alutLoadWAVFile)( ALbyte *file,ALenum *format,ALvoid **data,ALsizei *size,ALsizei *freq,ALboolean *loop ); + void (ALUTAPIENTRY *alutLoadWAVMemory)( ALbyte *memory,ALenum *format,ALvoid **data,ALsizei *size,ALsizei *freq,ALboolean *loop ); +#else + void (ALUTAPIENTRY *alutLoadWAVFile( ALbyte *file,ALenum *format,ALvoid **data,ALsizei *size,ALsizei *freq ); + void (ALUTAPIENTRY *alutLoadWAVMemory)( ALbyte *memory,ALenum *format,ALvoid **data,ALsizei *size,ALsizei *freq ); +#endif + void (ALUTAPIENTRY *alutUnloadWAV)( ALenum format,ALvoid *data,ALsizei size,ALsizei freq ); + +#endif /* ALUT_NO_PROTOTYPES */ + +#ifdef TARGET_OS_MAC +#if TARGET_OS_MAC +#pragma export off +#endif /* TARGET_OS_MAC */ +#endif /* TARGET_OS_MAC */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/reaction/engine/code/SDL12/include/SDL.h b/reaction/engine/code/SDL12/include/SDL.h new file mode 100644 index 00000000..60ac26ce --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL.h @@ -0,0 +1,94 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* Main include header for the SDL library */ + +#ifndef _SDL_H +#define _SDL_H + +#include "SDL_main.h" +#include "SDL_stdinc.h" +#include "SDL_audio.h" +#include "SDL_cdrom.h" +#include "SDL_cpuinfo.h" +#include "SDL_endian.h" +#include "SDL_error.h" +#include "SDL_events.h" +#include "SDL_loadso.h" +#include "SDL_mutex.h" +#include "SDL_rwops.h" +#include "SDL_thread.h" +#include "SDL_timer.h" +#include "SDL_video.h" +#include "SDL_version.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* As of version 0.5, SDL is loaded dynamically into the application */ + +/* These are the flags which may be passed to SDL_Init() -- you should + specify the subsystems which you will be using in your application. +*/ +#define SDL_INIT_TIMER 0x00000001 +#define SDL_INIT_AUDIO 0x00000010 +#define SDL_INIT_VIDEO 0x00000020 +#define SDL_INIT_CDROM 0x00000100 +#define SDL_INIT_JOYSTICK 0x00000200 +#define SDL_INIT_NOPARACHUTE 0x00100000 /* Don't catch fatal signals */ +#define SDL_INIT_EVENTTHREAD 0x01000000 /* Not supported on all OS's */ +#define SDL_INIT_EVERYTHING 0x0000FFFF + +/* This function loads the SDL dynamically linked library and initializes + * the subsystems specified by 'flags' (and those satisfying dependencies) + * Unless the SDL_INIT_NOPARACHUTE flag is set, it will install cleanup + * signal handlers for some commonly ignored fatal signals (like SIGSEGV) + */ +extern DECLSPEC int SDLCALL SDL_Init(Uint32 flags); + +/* This function initializes specific SDL subsystems */ +extern DECLSPEC int SDLCALL SDL_InitSubSystem(Uint32 flags); + +/* This function cleans up specific SDL subsystems */ +extern DECLSPEC void SDLCALL SDL_QuitSubSystem(Uint32 flags); + +/* This function returns mask of the specified subsystems which have + been initialized. + If 'flags' is 0, it returns a mask of all initialized subsystems. +*/ +extern DECLSPEC Uint32 SDLCALL SDL_WasInit(Uint32 flags); + +/* This function cleans up all initialized subsystems and unloads the + * dynamically linked library. You should call it upon all exit conditions. + */ +extern DECLSPEC void SDLCALL SDL_Quit(void); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_H */ diff --git a/reaction/engine/code/SDL12/include/SDL_active.h b/reaction/engine/code/SDL12/include/SDL_active.h new file mode 100644 index 00000000..2cf474c5 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_active.h @@ -0,0 +1,58 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* Include file for SDL application focus event handling */ + +#ifndef _SDL_active_h +#define _SDL_active_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* The available application states */ +#define SDL_APPMOUSEFOCUS 0x01 /* The app has mouse coverage */ +#define SDL_APPINPUTFOCUS 0x02 /* The app has input focus */ +#define SDL_APPACTIVE 0x04 /* The application is active */ + +/* Function prototypes */ +/* + * This function returns the current state of the application, which is a + * bitwise combination of SDL_APPMOUSEFOCUS, SDL_APPINPUTFOCUS, and + * SDL_APPACTIVE. If SDL_APPACTIVE is set, then the user is able to + * see your application, otherwise it has been iconified or disabled. + */ +extern DECLSPEC Uint8 SDLCALL SDL_GetAppState(void); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_active_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_audio.h b/reaction/engine/code/SDL12/include/SDL_audio.h new file mode 100644 index 00000000..68ec4759 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_audio.h @@ -0,0 +1,253 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* Access to the raw audio mixing buffer for the SDL library */ + +#ifndef _SDL_audio_h +#define _SDL_audio_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_endian.h" +#include "SDL_mutex.h" +#include "SDL_thread.h" +#include "SDL_rwops.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* The calculated values in this structure are calculated by SDL_OpenAudio() */ +typedef struct SDL_AudioSpec { + int freq; /* DSP frequency -- samples per second */ + Uint16 format; /* Audio data format */ + Uint8 channels; /* Number of channels: 1 mono, 2 stereo */ + Uint8 silence; /* Audio buffer silence value (calculated) */ + Uint16 samples; /* Audio buffer size in samples (power of 2) */ + Uint16 padding; /* Necessary for some compile environments */ + Uint32 size; /* Audio buffer size in bytes (calculated) */ + /* This function is called when the audio device needs more data. + 'stream' is a pointer to the audio data buffer + 'len' is the length of that buffer in bytes. + Once the callback returns, the buffer will no longer be valid. + Stereo samples are stored in a LRLRLR ordering. + */ + void (SDLCALL *callback)(void *userdata, Uint8 *stream, int len); + void *userdata; +} SDL_AudioSpec; + +/* Audio format flags (defaults to LSB byte order) */ +#define AUDIO_U8 0x0008 /* Unsigned 8-bit samples */ +#define AUDIO_S8 0x8008 /* Signed 8-bit samples */ +#define AUDIO_U16LSB 0x0010 /* Unsigned 16-bit samples */ +#define AUDIO_S16LSB 0x8010 /* Signed 16-bit samples */ +#define AUDIO_U16MSB 0x1010 /* As above, but big-endian byte order */ +#define AUDIO_S16MSB 0x9010 /* As above, but big-endian byte order */ +#define AUDIO_U16 AUDIO_U16LSB +#define AUDIO_S16 AUDIO_S16LSB + +/* Native audio byte ordering */ +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define AUDIO_U16SYS AUDIO_U16LSB +#define AUDIO_S16SYS AUDIO_S16LSB +#else +#define AUDIO_U16SYS AUDIO_U16MSB +#define AUDIO_S16SYS AUDIO_S16MSB +#endif + + +/* A structure to hold a set of audio conversion filters and buffers */ +typedef struct SDL_AudioCVT { + int needed; /* Set to 1 if conversion possible */ + Uint16 src_format; /* Source audio format */ + Uint16 dst_format; /* Target audio format */ + double rate_incr; /* Rate conversion increment */ + Uint8 *buf; /* Buffer to hold entire audio data */ + int len; /* Length of original audio buffer */ + int len_cvt; /* Length of converted audio buffer */ + int len_mult; /* buffer must be len*len_mult big */ + double len_ratio; /* Given len, final size is len*len_ratio */ + void (SDLCALL *filters[10])(struct SDL_AudioCVT *cvt, Uint16 format); + int filter_index; /* Current audio conversion function */ +} SDL_AudioCVT; + + +/* Function prototypes */ + +/* These functions are used internally, and should not be used unless you + * have a specific need to specify the audio driver you want to use. + * You should normally use SDL_Init() or SDL_InitSubSystem(). + */ +extern DECLSPEC int SDLCALL SDL_AudioInit(const char *driver_name); +extern DECLSPEC void SDLCALL SDL_AudioQuit(void); + +/* This function fills the given character buffer with the name of the + * current audio driver, and returns a pointer to it if the audio driver has + * been initialized. It returns NULL if no driver has been initialized. + */ +extern DECLSPEC char * SDLCALL SDL_AudioDriverName(char *namebuf, int maxlen); + +/* + * This function opens the audio device with the desired parameters, and + * returns 0 if successful, placing the actual hardware parameters in the + * structure pointed to by 'obtained'. If 'obtained' is NULL, the audio + * data passed to the callback function will be guaranteed to be in the + * requested format, and will be automatically converted to the hardware + * audio format if necessary. This function returns -1 if it failed + * to open the audio device, or couldn't set up the audio thread. + * + * When filling in the desired audio spec structure, + * 'desired->freq' should be the desired audio frequency in samples-per-second. + * 'desired->format' should be the desired audio format. + * 'desired->samples' is the desired size of the audio buffer, in samples. + * This number should be a power of two, and may be adjusted by the audio + * driver to a value more suitable for the hardware. Good values seem to + * range between 512 and 8096 inclusive, depending on the application and + * CPU speed. Smaller values yield faster response time, but can lead + * to underflow if the application is doing heavy processing and cannot + * fill the audio buffer in time. A stereo sample consists of both right + * and left channels in LR ordering. + * Note that the number of samples is directly related to time by the + * following formula: ms = (samples*1000)/freq + * 'desired->size' is the size in bytes of the audio buffer, and is + * calculated by SDL_OpenAudio(). + * 'desired->silence' is the value used to set the buffer to silence, + * and is calculated by SDL_OpenAudio(). + * 'desired->callback' should be set to a function that will be called + * when the audio device is ready for more data. It is passed a pointer + * to the audio buffer, and the length in bytes of the audio buffer. + * This function usually runs in a separate thread, and so you should + * protect data structures that it accesses by calling SDL_LockAudio() + * and SDL_UnlockAudio() in your code. + * 'desired->userdata' is passed as the first parameter to your callback + * function. + * + * The audio device starts out playing silence when it's opened, and should + * be enabled for playing by calling SDL_PauseAudio(0) when you are ready + * for your audio callback function to be called. Since the audio driver + * may modify the requested size of the audio buffer, you should allocate + * any local mixing buffers after you open the audio device. + */ +extern DECLSPEC int SDLCALL SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained); + +/* + * Get the current audio state: + */ +typedef enum { + SDL_AUDIO_STOPPED = 0, + SDL_AUDIO_PLAYING, + SDL_AUDIO_PAUSED +} SDL_audiostatus; +extern DECLSPEC SDL_audiostatus SDLCALL SDL_GetAudioStatus(void); + +/* + * This function pauses and unpauses the audio callback processing. + * It should be called with a parameter of 0 after opening the audio + * device to start playing sound. This is so you can safely initialize + * data for your callback function after opening the audio device. + * Silence will be written to the audio device during the pause. + */ +extern DECLSPEC void SDLCALL SDL_PauseAudio(int pause_on); + +/* + * This function loads a WAVE from the data source, automatically freeing + * that source if 'freesrc' is non-zero. For example, to load a WAVE file, + * you could do: + * SDL_LoadWAV_RW(SDL_RWFromFile("sample.wav", "rb"), 1, ...); + * + * If this function succeeds, it returns the given SDL_AudioSpec, + * filled with the audio data format of the wave data, and sets + * 'audio_buf' to a malloc()'d buffer containing the audio data, + * and sets 'audio_len' to the length of that audio buffer, in bytes. + * You need to free the audio buffer with SDL_FreeWAV() when you are + * done with it. + * + * This function returns NULL and sets the SDL error message if the + * wave file cannot be opened, uses an unknown data format, or is + * corrupt. Currently raw and MS-ADPCM WAVE files are supported. + */ +extern DECLSPEC SDL_AudioSpec * SDLCALL SDL_LoadWAV_RW(SDL_RWops *src, int freesrc, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len); + +/* Compatibility convenience function -- loads a WAV from a file */ +#define SDL_LoadWAV(file, spec, audio_buf, audio_len) \ + SDL_LoadWAV_RW(SDL_RWFromFile(file, "rb"),1, spec,audio_buf,audio_len) + +/* + * This function frees data previously allocated with SDL_LoadWAV_RW() + */ +extern DECLSPEC void SDLCALL SDL_FreeWAV(Uint8 *audio_buf); + +/* + * This function takes a source format and rate and a destination format + * and rate, and initializes the 'cvt' structure with information needed + * by SDL_ConvertAudio() to convert a buffer of audio data from one format + * to the other. + * This function returns 0, or -1 if there was an error. + */ +extern DECLSPEC int SDLCALL SDL_BuildAudioCVT(SDL_AudioCVT *cvt, + Uint16 src_format, Uint8 src_channels, int src_rate, + Uint16 dst_format, Uint8 dst_channels, int dst_rate); + +/* Once you have initialized the 'cvt' structure using SDL_BuildAudioCVT(), + * created an audio buffer cvt->buf, and filled it with cvt->len bytes of + * audio data in the source format, this function will convert it in-place + * to the desired format. + * The data conversion may expand the size of the audio data, so the buffer + * cvt->buf should be allocated after the cvt structure is initialized by + * SDL_BuildAudioCVT(), and should be cvt->len*cvt->len_mult bytes long. + */ +extern DECLSPEC int SDLCALL SDL_ConvertAudio(SDL_AudioCVT *cvt); + +/* + * This takes two audio buffers of the playing audio format and mixes + * them, performing addition, volume adjustment, and overflow clipping. + * The volume ranges from 0 - 128, and should be set to SDL_MIX_MAXVOLUME + * for full audio volume. Note this does not change hardware volume. + * This is provided for convenience -- you can mix your own audio data. + */ +#define SDL_MIX_MAXVOLUME 128 +extern DECLSPEC void SDLCALL SDL_MixAudio(Uint8 *dst, const Uint8 *src, Uint32 len, int volume); + +/* + * The lock manipulated by these functions protects the callback function. + * During a LockAudio/UnlockAudio pair, you can be guaranteed that the + * callback function is not running. Do not call these from the callback + * function or you will cause deadlock. + */ +extern DECLSPEC void SDLCALL SDL_LockAudio(void); +extern DECLSPEC void SDLCALL SDL_UnlockAudio(void); + +/* + * This function shuts down audio processing and closes the audio device. + */ +extern DECLSPEC void SDLCALL SDL_CloseAudio(void); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_audio_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_byteorder.h b/reaction/engine/code/SDL12/include/SDL_byteorder.h new file mode 100644 index 00000000..3871cfed --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_byteorder.h @@ -0,0 +1,24 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* DEPRECATED */ +#include "SDL_endian.h" diff --git a/reaction/engine/code/SDL12/include/SDL_cdrom.h b/reaction/engine/code/SDL12/include/SDL_cdrom.h new file mode 100644 index 00000000..5f8f0c62 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_cdrom.h @@ -0,0 +1,171 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* This is the CD-audio control API for Simple DirectMedia Layer */ + +#ifndef _SDL_cdrom_h +#define _SDL_cdrom_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* In order to use these functions, SDL_Init() must have been called + with the SDL_INIT_CDROM flag. This causes SDL to scan the system + for CD-ROM drives, and load appropriate drivers. +*/ + +/* The maximum number of CD-ROM tracks on a disk */ +#define SDL_MAX_TRACKS 99 + +/* The types of CD-ROM track possible */ +#define SDL_AUDIO_TRACK 0x00 +#define SDL_DATA_TRACK 0x04 + +/* The possible states which a CD-ROM drive can be in. */ +typedef enum { + CD_TRAYEMPTY, + CD_STOPPED, + CD_PLAYING, + CD_PAUSED, + CD_ERROR = -1 +} CDstatus; + +/* Given a status, returns true if there's a disk in the drive */ +#define CD_INDRIVE(status) ((int)(status) > 0) + +typedef struct SDL_CDtrack { + Uint8 id; /* Track number */ + Uint8 type; /* Data or audio track */ + Uint16 unused; + Uint32 length; /* Length, in frames, of this track */ + Uint32 offset; /* Offset, in frames, from start of disk */ +} SDL_CDtrack; + +/* This structure is only current as of the last call to SDL_CDStatus() */ +typedef struct SDL_CD { + int id; /* Private drive identifier */ + CDstatus status; /* Current drive status */ + + /* The rest of this structure is only valid if there's a CD in drive */ + int numtracks; /* Number of tracks on disk */ + int cur_track; /* Current track position */ + int cur_frame; /* Current frame offset within current track */ + SDL_CDtrack track[SDL_MAX_TRACKS+1]; +} SDL_CD; + +/* Conversion functions from frames to Minute/Second/Frames and vice versa */ +#define CD_FPS 75 +#define FRAMES_TO_MSF(f, M,S,F) { \ + int value = f; \ + *(F) = value%CD_FPS; \ + value /= CD_FPS; \ + *(S) = value%60; \ + value /= 60; \ + *(M) = value; \ +} +#define MSF_TO_FRAMES(M, S, F) ((M)*60*CD_FPS+(S)*CD_FPS+(F)) + +/* CD-audio API functions: */ + +/* Returns the number of CD-ROM drives on the system, or -1 if + SDL_Init() has not been called with the SDL_INIT_CDROM flag. + */ +extern DECLSPEC int SDLCALL SDL_CDNumDrives(void); + +/* Returns a human-readable, system-dependent identifier for the CD-ROM. + Example: + "/dev/cdrom" + "E:" + "/dev/disk/ide/1/master" +*/ +extern DECLSPEC const char * SDLCALL SDL_CDName(int drive); + +/* Opens a CD-ROM drive for access. It returns a drive handle on success, + or NULL if the drive was invalid or busy. This newly opened CD-ROM + becomes the default CD used when other CD functions are passed a NULL + CD-ROM handle. + Drives are numbered starting with 0. Drive 0 is the system default CD-ROM. +*/ +extern DECLSPEC SDL_CD * SDLCALL SDL_CDOpen(int drive); + +/* This function returns the current status of the given drive. + If the drive has a CD in it, the table of contents of the CD and current + play position of the CD will be stored in the SDL_CD structure. +*/ +extern DECLSPEC CDstatus SDLCALL SDL_CDStatus(SDL_CD *cdrom); + +/* Play the given CD starting at 'start_track' and 'start_frame' for 'ntracks' + tracks and 'nframes' frames. If both 'ntrack' and 'nframe' are 0, play + until the end of the CD. This function will skip data tracks. + This function should only be called after calling SDL_CDStatus() to + get track information about the CD. + For example: + // Play entire CD: + if ( CD_INDRIVE(SDL_CDStatus(cdrom)) ) + SDL_CDPlayTracks(cdrom, 0, 0, 0, 0); + // Play last track: + if ( CD_INDRIVE(SDL_CDStatus(cdrom)) ) { + SDL_CDPlayTracks(cdrom, cdrom->numtracks-1, 0, 0, 0); + } + // Play first and second track and 10 seconds of third track: + if ( CD_INDRIVE(SDL_CDStatus(cdrom)) ) + SDL_CDPlayTracks(cdrom, 0, 0, 2, 10); + + This function returns 0, or -1 if there was an error. +*/ +extern DECLSPEC int SDLCALL SDL_CDPlayTracks(SDL_CD *cdrom, + int start_track, int start_frame, int ntracks, int nframes); + +/* Play the given CD starting at 'start' frame for 'length' frames. + It returns 0, or -1 if there was an error. +*/ +extern DECLSPEC int SDLCALL SDL_CDPlay(SDL_CD *cdrom, int start, int length); + +/* Pause play -- returns 0, or -1 on error */ +extern DECLSPEC int SDLCALL SDL_CDPause(SDL_CD *cdrom); + +/* Resume play -- returns 0, or -1 on error */ +extern DECLSPEC int SDLCALL SDL_CDResume(SDL_CD *cdrom); + +/* Stop play -- returns 0, or -1 on error */ +extern DECLSPEC int SDLCALL SDL_CDStop(SDL_CD *cdrom); + +/* Eject CD-ROM -- returns 0, or -1 on error */ +extern DECLSPEC int SDLCALL SDL_CDEject(SDL_CD *cdrom); + +/* Closes the handle for the CD-ROM drive */ +extern DECLSPEC void SDLCALL SDL_CDClose(SDL_CD *cdrom); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_video_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_config.h b/reaction/engine/code/SDL12/include/SDL_config.h new file mode 100644 index 00000000..8970ec3d --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_config.h @@ -0,0 +1,45 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifndef _SDL_config_h +#define _SDL_config_h + +#include "SDL_platform.h" + +/* Add any platform that doesn't build using the configure system */ +#if defined(__AMIGA__) +#include "SDL_config_amiga.h" +#elif defined(__DREAMCAST__) +#include "SDL_config_dreamcast.h" +#elif defined(__MACOS__) +#include "SDL_config_macos.h" +#elif defined(__MACOSX__) +#include "SDL_config_macosx.h" +#elif defined(__WIN32__) +#include "SDL_config_win32.h" +#elif defined(__OS2__) +#include "SDL_config_os2.h" +#else +#include "SDL_config_minimal.h" +#endif /* platform config */ + +#endif /* _SDL_config_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_config_amiga.h b/reaction/engine/code/SDL12/include/SDL_config_amiga.h new file mode 100644 index 00000000..23e08619 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_config_amiga.h @@ -0,0 +1,80 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifndef _SDL_config_amiga_h +#define _SDL_config_amiga_h + +#include "SDL_platform.h" + +/* This is a set of defines to configure the SDL features */ + +#define SDL_HAS_64BIT_TYPE 1 + +/* Useful headers */ +#define HAVE_SYS_TYPES_H 1 +#define HAVE_STDIO_H 1 +#define STDC_HEADERS 1 +#define HAVE_STRING_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_SIGNAL_H 1 + +/* C library functions */ +#define HAVE_MALLOC 1 +#define HAVE_CALLOC 1 +#define HAVE_REALLOC 1 +#define HAVE_FREE 1 +#define HAVE_ALLOCA 1 +#define HAVE_GETENV 1 +#define HAVE_PUTENV 1 +#define HAVE_MEMSET 1 +#define HAVE_MEMCPY 1 +#define HAVE_MEMMOVE 1 +#define HAVE_MEMCMP 1 + +/* Enable various audio drivers */ +#define SDL_AUDIO_DRIVER_AHI 1 +#define SDL_AUDIO_DRIVER_DISK 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 + +/* Enable various cdrom drivers */ +#define SDL_CDROM_DUMMY 1 + +/* Enable various input drivers */ +#define SDL_JOYSTICK_AMIGA 1 + +/* Enable various shared object loading systems */ +#define SDL_LOADSO_DUMMY 1 + +/* Enable various threading systems */ +#define SDL_THREAD_AMIGA 1 + +/* Enable various timer systems */ +#define SDL_TIMER_AMIGA 1 + +/* Enable various video drivers */ +#define SDL_VIDEO_DRIVER_CYBERGRAPHICS 1 +#define SDL_VIDEO_DRIVER_DUMMY 1 + +/* Enable OpenGL support */ +#define SDL_VIDEO_OPENGL 1 + +#endif /* _SDL_config_amiga_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_config_dreamcast.h b/reaction/engine/code/SDL12/include/SDL_config_dreamcast.h new file mode 100644 index 00000000..9cbeea31 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_config_dreamcast.h @@ -0,0 +1,106 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifndef _SDL_config_dreamcast_h +#define _SDL_config_dreamcast_h + +#include "SDL_platform.h" + +/* This is a set of defines to configure the SDL features */ + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed int int32_t; +typedef unsigned int uint32_t; +typedef signed long long int64_t; +typedef unsigned long long uint64_t; +typedef unsigned long uintptr_t; +#define SDL_HAS_64BIT_TYPE 1 + +/* Useful headers */ +#define HAVE_SYS_TYPES_H 1 +#define HAVE_STDIO_H 1 +#define STDC_HEADERS 1 +#define HAVE_STRING_H 1 +#define HAVE_CTYPE_H 1 + +/* C library functions */ +#define HAVE_MALLOC 1 +#define HAVE_CALLOC 1 +#define HAVE_REALLOC 1 +#define HAVE_FREE 1 +#define HAVE_ALLOCA 1 +#define HAVE_GETENV 1 +#define HAVE_PUTENV 1 +#define HAVE_QSORT 1 +#define HAVE_ABS 1 +#define HAVE_BCOPY 1 +#define HAVE_MEMSET 1 +#define HAVE_MEMCPY 1 +#define HAVE_MEMMOVE 1 +#define HAVE_MEMCMP 1 +#define HAVE_STRLEN 1 +#define HAVE_STRDUP 1 +#define HAVE_INDEX 1 +#define HAVE_RINDEX 1 +#define HAVE_STRCHR 1 +#define HAVE_STRRCHR 1 +#define HAVE_STRSTR 1 +#define HAVE_STRTOL 1 +#define HAVE_STRTOD 1 +#define HAVE_ATOI 1 +#define HAVE_ATOF 1 +#define HAVE_STRCMP 1 +#define HAVE_STRNCMP 1 +#define HAVE_STRICMP 1 +#define HAVE_STRCASECMP 1 +#define HAVE_SSCANF 1 +#define HAVE_SNPRINTF 1 +#define HAVE_VSNPRINTF 1 + +/* Enable various audio drivers */ +#define SDL_AUDIO_DRIVER_DC 1 +#define SDL_AUDIO_DRIVER_DISK 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 + +/* Enable various cdrom drivers */ +#define SDL_CDROM_DC 1 + +/* Enable various input drivers */ +#define SDL_JOYSTICK_DC 1 + +/* Enable various shared object loading systems */ +#define SDL_LOADSO_DUMMY 1 + +/* Enable various threading systems */ +#define SDL_THREAD_DC 1 + +/* Enable various timer systems */ +#define SDL_TIMER_DC 1 + +/* Enable various video drivers */ +#define SDL_VIDEO_DRIVER_DC 1 +#define SDL_VIDEO_DRIVER_DUMMY 1 + +#endif /* _SDL_config_dreamcast_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_config_macos.h b/reaction/engine/code/SDL12/include/SDL_config_macos.h new file mode 100644 index 00000000..c4a1c598 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_config_macos.h @@ -0,0 +1,112 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifndef _SDL_config_macos_h +#define _SDL_config_macos_h + +#include "SDL_platform.h" + +/* This is a set of defines to configure the SDL features */ + +#include + +typedef SInt8 int8_t; +typedef UInt8 uint8_t; +typedef SInt16 int16_t; +typedef UInt16 uint16_t; +typedef SInt32 int32_t; +typedef UInt32 uint32_t; +typedef SInt64 int64_t; +typedef UInt64 uint64_t; +typedef unsigned long uintptr_t; + +#define SDL_HAS_64BIT_TYPE 1 + +/* Useful headers */ +#define HAVE_STDIO_H 1 +#define STDC_HEADERS 1 +#define HAVE_STRING_H 1 +#define HAVE_CTYPE_H 1 +#define HAVE_MATH_H 1 +#define HAVE_SIGNAL_H 1 + +/* C library functions */ +#define HAVE_MALLOC 1 +#define HAVE_CALLOC 1 +#define HAVE_REALLOC 1 +#define HAVE_FREE 1 +#define HAVE_ALLOCA 1 +#define HAVE_ABS 1 +#define HAVE_MEMSET 1 +#define HAVE_MEMCPY 1 +#define HAVE_MEMMOVE 1 +#define HAVE_MEMCMP 1 +#define HAVE_STRLEN 1 +#define HAVE_STRCHR 1 +#define HAVE_STRRCHR 1 +#define HAVE_STRSTR 1 +#define HAVE_ITOA 1 +#define HAVE_STRTOL 1 +#define HAVE_STRTOD 1 +#define HAVE_ATOI 1 +#define HAVE_ATOF 1 +#define HAVE_STRCMP 1 +#define HAVE_STRNCMP 1 +#define HAVE_SSCANF 1 + +/* Enable various audio drivers */ +#define SDL_AUDIO_DRIVER_SNDMGR 1 +#define SDL_AUDIO_DRIVER_DISK 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 + +/* Enable various cdrom drivers */ +#if TARGET_API_MAC_CARBON +#define SDL_CDROM_DUMMY 1 +#else +#define SDL_CDROM_MACOS 1 +#endif + +/* Enable various input drivers */ +#if TARGET_API_MAC_CARBON +#define SDL_JOYSTICK_DUMMY 1 +#else +#define SDL_JOYSTICK_MACOS 1 +#endif + +/* Enable various shared object loading systems */ +#define SDL_LOADSO_MACOS 1 + +/* Enable various threading systems */ +#define SDL_THREADS_DISABLED 1 + +/* Enable various timer systems */ +#define SDL_TIMER_MACOS 1 + +/* Enable various video drivers */ +#define SDL_VIDEO_DRIVER_DUMMY 1 +#define SDL_VIDEO_DRIVER_DRAWSPROCKET 1 +#define SDL_VIDEO_DRIVER_TOOLBOX 1 + +/* Enable OpenGL support */ +#define SDL_VIDEO_OPENGL 1 + +#endif /* _SDL_config_macos_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_config_macosx.h b/reaction/engine/code/SDL12/include/SDL_config_macosx.h new file mode 100644 index 00000000..8f04930d --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_config_macosx.h @@ -0,0 +1,132 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifndef _SDL_config_macosx_h +#define _SDL_config_macosx_h + +#include "SDL_platform.h" + +/* This is a set of defines to configure the SDL features */ + +#define SDL_HAS_64BIT_TYPE 1 + +/* Useful headers */ +/* If we specified an SDK or have a post-PowerPC chip, then alloca.h exists. */ +#if ( (MAC_OS_X_VERSION_MIN_REQUIRED >= 1030) || (!defined(__POWERPC__)) ) +#define HAVE_ALLOCA_H 1 +#endif +#define HAVE_SYS_TYPES_H 1 +#define HAVE_STDIO_H 1 +#define STDC_HEADERS 1 +#define HAVE_STRING_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_CTYPE_H 1 +#define HAVE_MATH_H 1 +#define HAVE_SIGNAL_H 1 + +/* C library functions */ +#define HAVE_MALLOC 1 +#define HAVE_CALLOC 1 +#define HAVE_REALLOC 1 +#define HAVE_FREE 1 +#define HAVE_ALLOCA 1 +#define HAVE_GETENV 1 +#define HAVE_PUTENV 1 +#define HAVE_UNSETENV 1 +#define HAVE_QSORT 1 +#define HAVE_ABS 1 +#define HAVE_BCOPY 1 +#define HAVE_MEMSET 1 +#define HAVE_MEMCPY 1 +#define HAVE_MEMMOVE 1 +#define HAVE_MEMCMP 1 +#define HAVE_STRLEN 1 +#define HAVE_STRLCPY 1 +#define HAVE_STRLCAT 1 +#define HAVE_STRDUP 1 +#define HAVE_STRCHR 1 +#define HAVE_STRRCHR 1 +#define HAVE_STRSTR 1 +#define HAVE_STRTOL 1 +#define HAVE_STRTOUL 1 +#define HAVE_STRTOLL 1 +#define HAVE_STRTOULL 1 +#define HAVE_STRTOD 1 +#define HAVE_ATOI 1 +#define HAVE_ATOF 1 +#define HAVE_STRCMP 1 +#define HAVE_STRNCMP 1 +#define HAVE_STRCASECMP 1 +#define HAVE_STRNCASECMP 1 +#define HAVE_SSCANF 1 +#define HAVE_SNPRINTF 1 +#define HAVE_VSNPRINTF 1 +#define HAVE_SIGACTION 1 +#define HAVE_SETJMP 1 +#define HAVE_NANOSLEEP 1 + +/* Enable various audio drivers */ +#define SDL_AUDIO_DRIVER_COREAUDIO 1 +#define SDL_AUDIO_DRIVER_SNDMGR 1 +#define SDL_AUDIO_DRIVER_DISK 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 + +/* Enable various cdrom drivers */ +#define SDL_CDROM_MACOSX 1 + +/* Enable various input drivers */ +#define SDL_JOYSTICK_IOKIT 1 + +/* Enable various shared object loading systems */ +#ifdef __ppc__ +/* For Mac OS X 10.2 compatibility */ +#define SDL_LOADSO_DLCOMPAT 1 +#else +#define SDL_LOADSO_DLOPEN 1 +#endif + +/* Enable various threading systems */ +#define SDL_THREAD_PTHREAD 1 +#define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX 1 + +/* Enable various timer systems */ +#define SDL_TIMER_UNIX 1 + +/* Enable various video drivers */ +#define SDL_VIDEO_DRIVER_DUMMY 1 +#if TARGET_API_MAC_CARBON +#define SDL_VIDEO_DRIVER_TOOLBOX 1 +#else +#define SDL_VIDEO_DRIVER_QUARTZ 1 +#endif + +/* Enable OpenGL support */ +#define SDL_VIDEO_OPENGL 1 + +/* Enable assembly routines */ +#define SDL_ASSEMBLY_ROUTINES 1 +#ifdef __ppc__ +#define SDL_ALTIVEC_BLITTERS 1 +#endif + +#endif /* _SDL_config_macosx_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_config_minimal.h b/reaction/engine/code/SDL12/include/SDL_config_minimal.h new file mode 100644 index 00000000..78b6148c --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_config_minimal.h @@ -0,0 +1,62 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifndef _SDL_config_minimal_h +#define _SDL_config_minimal_h + +#include "SDL_platform.h" + +/* This is the minimal configuration that can be used to build SDL */ + +#include + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed int int32_t; +typedef unsigned int uint32_t; +typedef unsigned int size_t; +typedef unsigned long uintptr_t; + +/* Enable the dummy audio driver (src/audio/dummy/\*.c) */ +#define SDL_AUDIO_DRIVER_DUMMY 1 + +/* Enable the stub cdrom driver (src/cdrom/dummy/\*.c) */ +#define SDL_CDROM_DISABLED 1 + +/* Enable the stub joystick driver (src/joystick/dummy/\*.c) */ +#define SDL_JOYSTICK_DISABLED 1 + +/* Enable the stub shared object loader (src/loadso/dummy/\*.c) */ +#define SDL_LOADSO_DISABLED 1 + +/* Enable the stub thread support (src/thread/generic/\*.c) */ +#define SDL_THREADS_DISABLED 1 + +/* Enable the stub timer support (src/timer/dummy/\*.c) */ +#define SDL_TIMERS_DISABLED 1 + +/* Enable the dummy video driver (src/video/dummy/\*.c) */ +#define SDL_VIDEO_DRIVER_DUMMY 1 + +#endif /* _SDL_config_minimal_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_config_os2.h b/reaction/engine/code/SDL12/include/SDL_config_os2.h new file mode 100644 index 00000000..8cdea9ff --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_config_os2.h @@ -0,0 +1,141 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifndef _SDL_config_os2_h +#define _SDL_config_os2_h + +#include "SDL_platform.h" + +/* This is a set of defines to configure the SDL features */ + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed int int32_t; +typedef unsigned int uint32_t; +typedef unsigned int size_t; +typedef unsigned long uintptr_t; +typedef signed long long int64_t; +typedef unsigned long long uint64_t; + +#define SDL_HAS_64BIT_TYPE 1 + +/* Use Watcom's LIBC */ +#define HAVE_LIBC 1 + +/* Useful headers */ +#define HAVE_SYS_TYPES_H 1 +#define HAVE_STDIO_H 1 +#define STDC_HEADERS 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STDARG_H 1 +#define HAVE_MALLOC_H 1 +#define HAVE_MEMORY_H 1 +#define HAVE_STRING_H 1 +#define HAVE_STRINGS_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_CTYPE_H 1 +#define HAVE_MATH_H 1 +#define HAVE_SIGNAL_H 1 + +/* C library functions */ +#define HAVE_MALLOC 1 +#define HAVE_CALLOC 1 +#define HAVE_REALLOC 1 +#define HAVE_FREE 1 +#define HAVE_ALLOCA 1 +#define HAVE_GETENV 1 +#define HAVE_PUTENV 1 +#define HAVE_UNSETENV 1 +#define HAVE_QSORT 1 +#define HAVE_ABS 1 +#define HAVE_BCOPY 1 +#define HAVE_MEMSET 1 +#define HAVE_MEMCPY 1 +#define HAVE_MEMMOVE 1 +#define HAVE_MEMCMP 1 +#define HAVE_STRLEN 1 +#define HAVE_STRLCPY 1 +#define HAVE_STRLCAT 1 +#define HAVE_STRDUP 1 +#define HAVE__STRREV 1 +#define HAVE__STRUPR 1 +#define HAVE__STRLWR 1 +#define HAVE_INDEX 1 +#define HAVE_RINDEX 1 +#define HAVE_STRCHR 1 +#define HAVE_STRRCHR 1 +#define HAVE_STRSTR 1 +#define HAVE_ITOA 1 +#define HAVE__LTOA 1 +#define HAVE__UITOA 1 +#define HAVE__ULTOA 1 +#define HAVE_STRTOL 1 +#define HAVE__I64TOA 1 +#define HAVE__UI64TOA 1 +#define HAVE_STRTOLL 1 +#define HAVE_STRTOD 1 +#define HAVE_ATOI 1 +#define HAVE_ATOF 1 +#define HAVE_STRCMP 1 +#define HAVE_STRNCMP 1 +#define HAVE_STRICMP 1 +#define HAVE_STRCASECMP 1 +#define HAVE_SSCANF 1 +#define HAVE_SNPRINTF 1 +#define HAVE_VSNPRINTF 1 +#define HAVE_SETJMP 1 +#define HAVE_CLOCK_GETTIME 1 + +/* Enable various audio drivers */ +#define SDL_AUDIO_DRIVER_DART 1 +#define SDL_AUDIO_DRIVER_DISK 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 + +/* Enable various cdrom drivers */ +#define SDL_CDROM_OS2 1 + +/* Enable various input drivers */ +#define SDL_JOYSTICK_OS2 1 + +/* Enable various shared object loading systems */ +#define SDL_LOADSO_OS2 1 + +/* Enable various threading systems */ +#define SDL_THREAD_OS2 1 + +/* Enable various timer systems */ +#define SDL_TIMER_OS2 1 + +/* Enable various video drivers */ +#define SDL_VIDEO_DRIVER_DUMMY 1 +#define SDL_VIDEO_DRIVER_OS2FS 1 + +/* Enable OpenGL support */ +/* Nothing here yet for OS/2... :( */ + +/* Enable assembly routines where available */ +#define SDL_ASSEMBLY_ROUTINES 1 + +#endif /* _SDL_config_os2_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_config_win32.h b/reaction/engine/code/SDL12/include/SDL_config_win32.h new file mode 100644 index 00000000..23e4868c --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_config_win32.h @@ -0,0 +1,173 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifndef _SDL_config_win32_h +#define _SDL_config_win32_h + +#include "SDL_platform.h" + +/* This is a set of defines to configure the SDL features */ + +#ifdef __GNUC__ +#define HAVE_STDINT_H 1 +#elif defined(_MSC_VER) +typedef signed __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef signed __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef signed __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; +#ifndef _UINTPTR_T_DEFINED +#ifdef _WIN64 +typedef unsigned __int64 uintptr_t; +#else +typedef unsigned int uintptr_t; +#endif +#define _UINTPTR_T_DEFINED +#endif +#else /* !__GNUC__ && !_MSC_VER */ +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed int int32_t; +typedef unsigned int uint32_t; +typedef signed long long int64_t; +typedef unsigned long long uint64_t; +#ifndef _SIZE_T_DEFINED_ +#define _SIZE_T_DEFINED_ +typedef unsigned int size_t; +#endif +typedef unsigned int uintptr_t; +#endif /* __GNUC__ || _MSC_VER */ +#define SDL_HAS_64BIT_TYPE 1 + +/* Enabled for SDL 1.2 (binary compatibility) */ +#define HAVE_LIBC 1 +#ifdef HAVE_LIBC +/* Useful headers */ +#define HAVE_STDIO_H 1 +#define STDC_HEADERS 1 +#define HAVE_STRING_H 1 +#define HAVE_CTYPE_H 1 +#define HAVE_MATH_H 1 +#ifndef _WIN32_WCE +#define HAVE_SIGNAL_H 1 +#endif + +/* C library functions */ +#define HAVE_MALLOC 1 +#define HAVE_CALLOC 1 +#define HAVE_REALLOC 1 +#define HAVE_FREE 1 +#define HAVE_ALLOCA 1 +#define HAVE_QSORT 1 +#define HAVE_ABS 1 +#define HAVE_MEMSET 1 +#define HAVE_MEMCPY 1 +#define HAVE_MEMMOVE 1 +#define HAVE_MEMCMP 1 +#define HAVE_STRLEN 1 +#define HAVE__STRREV 1 +#define HAVE__STRUPR 1 +#define HAVE__STRLWR 1 +#define HAVE_STRCHR 1 +#define HAVE_STRRCHR 1 +#define HAVE_STRSTR 1 +#define HAVE_ITOA 1 +#define HAVE__LTOA 1 +#define HAVE__ULTOA 1 +#define HAVE_STRTOL 1 +#define HAVE_STRTOUL 1 +#define HAVE_STRTOLL 1 +#define HAVE_STRTOD 1 +#define HAVE_ATOI 1 +#define HAVE_ATOF 1 +#define HAVE_STRCMP 1 +#define HAVE_STRNCMP 1 +#define HAVE__STRICMP 1 +#define HAVE__STRNICMP 1 +#define HAVE_SSCANF 1 +#else +#define HAVE_STDARG_H 1 +#define HAVE_STDDEF_H 1 +#endif + +/* Enable various audio drivers */ +#ifndef _WIN32_WCE +#define SDL_AUDIO_DRIVER_DSOUND 1 +#endif +#define SDL_AUDIO_DRIVER_WAVEOUT 1 +#define SDL_AUDIO_DRIVER_DISK 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 + +/* Enable various cdrom drivers */ +#ifdef _WIN32_WCE +#define SDL_CDROM_DISABLED 1 +#else +#define SDL_CDROM_WIN32 1 +#endif + +/* Enable various input drivers */ +#ifdef _WIN32_WCE +#define SDL_JOYSTICK_DISABLED 1 +#else +#define SDL_JOYSTICK_WINMM 1 +#endif + +/* Enable various shared object loading systems */ +#define SDL_LOADSO_WIN32 1 + +/* Enable various threading systems */ +#define SDL_THREAD_WIN32 1 + +/* Enable various timer systems */ +#ifdef _WIN32_WCE +#define SDL_TIMER_WINCE 1 +#else +#define SDL_TIMER_WIN32 1 +#endif + +/* Enable various video drivers */ +#ifdef _WIN32_WCE +#define SDL_VIDEO_DRIVER_GAPI 1 +#endif +#ifndef _WIN32_WCE +#define SDL_VIDEO_DRIVER_DDRAW 1 +#endif +#define SDL_VIDEO_DRIVER_DUMMY 1 +#define SDL_VIDEO_DRIVER_WINDIB 1 + +/* Enable OpenGL support */ +#ifndef _WIN32_WCE +#define SDL_VIDEO_OPENGL 1 +#define SDL_VIDEO_OPENGL_WGL 1 +#endif + +/* Enable assembly routines (Win64 doesn't have inline asm) */ +#ifndef _WIN64 +#define SDL_ASSEMBLY_ROUTINES 1 +#endif + +#endif /* _SDL_config_win32_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_copying.h b/reaction/engine/code/SDL12/include/SDL_copying.h new file mode 100644 index 00000000..39e122db --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_copying.h @@ -0,0 +1,22 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + diff --git a/reaction/engine/code/SDL12/include/SDL_cpuinfo.h b/reaction/engine/code/SDL12/include/SDL_cpuinfo.h new file mode 100644 index 00000000..72acbdd8 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_cpuinfo.h @@ -0,0 +1,75 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* CPU feature detection for SDL */ + +#ifndef _SDL_cpuinfo_h +#define _SDL_cpuinfo_h + +#include "SDL_stdinc.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* This function returns true if the CPU has the RDTSC instruction + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasRDTSC(void); + +/* This function returns true if the CPU has MMX features + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasMMX(void); + +/* This function returns true if the CPU has MMX Ext. features + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasMMXExt(void); + +/* This function returns true if the CPU has 3DNow features + */ +extern DECLSPEC SDL_bool SDLCALL SDL_Has3DNow(void); + +/* This function returns true if the CPU has 3DNow! Ext. features + */ +extern DECLSPEC SDL_bool SDLCALL SDL_Has3DNowExt(void); + +/* This function returns true if the CPU has SSE features + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasSSE(void); + +/* This function returns true if the CPU has SSE2 features + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasSSE2(void); + +/* This function returns true if the CPU has AltiVec features + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasAltiVec(void); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_cpuinfo_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_endian.h b/reaction/engine/code/SDL12/include/SDL_endian.h new file mode 100644 index 00000000..6257a649 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_endian.h @@ -0,0 +1,192 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* Functions for reading and writing endian-specific values */ + +#ifndef _SDL_endian_h +#define _SDL_endian_h + +#include "SDL_stdinc.h" + +/* The two types of endianness */ +#define SDL_LIL_ENDIAN 1234 +#define SDL_BIG_ENDIAN 4321 + +#ifndef SDL_BYTEORDER /* Not defined in SDL_config.h? */ +#if defined(__hppa__) || \ + defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \ + (defined(__MIPS__) && defined(__MISPEB__)) || \ + defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \ + defined(__sparc__) +#define SDL_BYTEORDER SDL_BIG_ENDIAN +#else +#define SDL_BYTEORDER SDL_LIL_ENDIAN +#endif +#endif /* !SDL_BYTEORDER */ + + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Use inline functions for compilers that support them, and static + functions for those that do not. Because these functions become + static for compilers that do not support inline functions, this + header should only be included in files that actually use them. +*/ +#if defined(__GNUC__) && defined(__i386__) && \ + !(__GNUC__ == 2 && __GNUC_MINOR__ == 95 /* broken gcc version */) +static __inline__ Uint16 SDL_Swap16(Uint16 x) +{ + __asm__("xchgb %b0,%h0" : "=q" (x) : "0" (x)); + return x; +} +#elif defined(__GNUC__) && defined(__x86_64__) +static __inline__ Uint16 SDL_Swap16(Uint16 x) +{ + __asm__("xchgb %b0,%h0" : "=Q" (x) : "0" (x)); + return x; +} +#elif defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__)) +static __inline__ Uint16 SDL_Swap16(Uint16 x) +{ + Uint16 result; + + __asm__("rlwimi %0,%2,8,16,23" : "=&r" (result) : "0" (x >> 8), "r" (x)); + return result; +} +#elif defined(__GNUC__) && (defined(__M68000__) || defined(__M68020__)) +static __inline__ Uint16 SDL_Swap16(Uint16 x) +{ + __asm__("rorw #8,%0" : "=d" (x) : "0" (x) : "cc"); + return x; +} +#else +static __inline__ Uint16 SDL_Swap16(Uint16 x) { + return((x<<8)|(x>>8)); +} +#endif + +#if defined(__GNUC__) && defined(__i386__) +static __inline__ Uint32 SDL_Swap32(Uint32 x) +{ + __asm__("bswap %0" : "=r" (x) : "0" (x)); + return x; +} +#elif defined(__GNUC__) && defined(__x86_64__) +static __inline__ Uint32 SDL_Swap32(Uint32 x) +{ + __asm__("bswapl %0" : "=r" (x) : "0" (x)); + return x; +} +#elif defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__)) +static __inline__ Uint32 SDL_Swap32(Uint32 x) +{ + Uint32 result; + + __asm__("rlwimi %0,%2,24,16,23" : "=&r" (result) : "0" (x>>24), "r" (x)); + __asm__("rlwimi %0,%2,8,8,15" : "=&r" (result) : "0" (result), "r" (x)); + __asm__("rlwimi %0,%2,24,0,7" : "=&r" (result) : "0" (result), "r" (x)); + return result; +} +#elif defined(__GNUC__) && (defined(__M68000__) || defined(__M68020__)) +static __inline__ Uint32 SDL_Swap32(Uint32 x) +{ + __asm__("rorw #8,%0\n\tswap %0\n\trorw #8,%0" : "=d" (x) : "0" (x) : "cc"); + return x; +} +#else +static __inline__ Uint32 SDL_Swap32(Uint32 x) { + return((x<<24)|((x<<8)&0x00FF0000)|((x>>8)&0x0000FF00)|(x>>24)); +} +#endif + +#ifdef SDL_HAS_64BIT_TYPE +#if defined(__GNUC__) && defined(__i386__) +static __inline__ Uint64 SDL_Swap64(Uint64 x) +{ + union { + struct { Uint32 a,b; } s; + Uint64 u; + } v; + v.u = x; + __asm__("bswapl %0 ; bswapl %1 ; xchgl %0,%1" + : "=r" (v.s.a), "=r" (v.s.b) + : "0" (v.s.a), "1" (v.s.b)); + return v.u; +} +#elif defined(__GNUC__) && defined(__x86_64__) +static __inline__ Uint64 SDL_Swap64(Uint64 x) +{ + __asm__("bswapq %0" : "=r" (x) : "0" (x)); + return x; +} +#else +static __inline__ Uint64 SDL_Swap64(Uint64 x) +{ + Uint32 hi, lo; + + /* Separate into high and low 32-bit values and swap them */ + lo = (Uint32)(x&0xFFFFFFFF); + x >>= 32; + hi = (Uint32)(x&0xFFFFFFFF); + x = SDL_Swap32(lo); + x <<= 32; + x |= SDL_Swap32(hi); + return(x); +} +#endif +#else +/* This is mainly to keep compilers from complaining in SDL code. + If there is no real 64-bit datatype, then compilers will complain about + the fake 64-bit datatype that SDL provides when it compiles user code. +*/ +#define SDL_Swap64(X) (X) +#endif /* SDL_HAS_64BIT_TYPE */ + + +/* Byteswap item from the specified endianness to the native endianness */ +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define SDL_SwapLE16(X) (X) +#define SDL_SwapLE32(X) (X) +#define SDL_SwapLE64(X) (X) +#define SDL_SwapBE16(X) SDL_Swap16(X) +#define SDL_SwapBE32(X) SDL_Swap32(X) +#define SDL_SwapBE64(X) SDL_Swap64(X) +#else +#define SDL_SwapLE16(X) SDL_Swap16(X) +#define SDL_SwapLE32(X) SDL_Swap32(X) +#define SDL_SwapLE64(X) SDL_Swap64(X) +#define SDL_SwapBE16(X) (X) +#define SDL_SwapBE32(X) (X) +#define SDL_SwapBE64(X) (X) +#endif + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_endian_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_error.h b/reaction/engine/code/SDL12/include/SDL_error.h new file mode 100644 index 00000000..26d6bfae --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_error.h @@ -0,0 +1,61 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* Simple error message routines for SDL */ + +#ifndef _SDL_error_h +#define _SDL_error_h + +#include "SDL_stdinc.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Public functions */ +extern DECLSPEC void SDLCALL SDL_SetError(const char *fmt, ...); +extern DECLSPEC char * SDLCALL SDL_GetError(void); +extern DECLSPEC void SDLCALL SDL_ClearError(void); + +/* Private error message function - used internally */ +#define SDL_OutOfMemory() SDL_Error(SDL_ENOMEM) +#define SDL_Unsupported() SDL_Error(SDL_UNSUPPORTED) +typedef enum { + SDL_ENOMEM, + SDL_EFREAD, + SDL_EFWRITE, + SDL_EFSEEK, + SDL_UNSUPPORTED, + SDL_LASTERROR +} SDL_errorcode; +extern DECLSPEC void SDLCALL SDL_Error(SDL_errorcode code); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_error_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_events.h b/reaction/engine/code/SDL12/include/SDL_events.h new file mode 100644 index 00000000..9fe918c7 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_events.h @@ -0,0 +1,337 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* Include file for SDL event handling */ + +#ifndef _SDL_events_h +#define _SDL_events_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_active.h" +#include "SDL_keyboard.h" +#include "SDL_mouse.h" +#include "SDL_joystick.h" +#include "SDL_quit.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* General keyboard/mouse state definitions */ +#define SDL_RELEASED 0 +#define SDL_PRESSED 1 + +/* Event enumerations */ +typedef enum { + SDL_NOEVENT = 0, /* Unused (do not remove) */ + SDL_ACTIVEEVENT, /* Application loses/gains visibility */ + SDL_KEYDOWN, /* Keys pressed */ + SDL_KEYUP, /* Keys released */ + SDL_MOUSEMOTION, /* Mouse moved */ + SDL_MOUSEBUTTONDOWN, /* Mouse button pressed */ + SDL_MOUSEBUTTONUP, /* Mouse button released */ + SDL_JOYAXISMOTION, /* Joystick axis motion */ + SDL_JOYBALLMOTION, /* Joystick trackball motion */ + SDL_JOYHATMOTION, /* Joystick hat position change */ + SDL_JOYBUTTONDOWN, /* Joystick button pressed */ + SDL_JOYBUTTONUP, /* Joystick button released */ + SDL_QUIT, /* User-requested quit */ + SDL_SYSWMEVENT, /* System specific event */ + SDL_EVENT_RESERVEDA, /* Reserved for future use.. */ + SDL_EVENT_RESERVEDB, /* Reserved for future use.. */ + SDL_VIDEORESIZE, /* User resized video mode */ + SDL_VIDEOEXPOSE, /* Screen needs to be redrawn */ + SDL_EVENT_RESERVED2, /* Reserved for future use.. */ + SDL_EVENT_RESERVED3, /* Reserved for future use.. */ + SDL_EVENT_RESERVED4, /* Reserved for future use.. */ + SDL_EVENT_RESERVED5, /* Reserved for future use.. */ + SDL_EVENT_RESERVED6, /* Reserved for future use.. */ + SDL_EVENT_RESERVED7, /* Reserved for future use.. */ + /* Events SDL_USEREVENT through SDL_MAXEVENTS-1 are for your use */ + SDL_USEREVENT = 24, + /* This last event is only for bounding internal arrays + It is the number of bits in the event mask datatype -- Uint32 + */ + SDL_NUMEVENTS = 32 +} SDL_EventType; + +/* Predefined event masks */ +#define SDL_EVENTMASK(X) (1<<(X)) +typedef enum { + SDL_ACTIVEEVENTMASK = SDL_EVENTMASK(SDL_ACTIVEEVENT), + SDL_KEYDOWNMASK = SDL_EVENTMASK(SDL_KEYDOWN), + SDL_KEYUPMASK = SDL_EVENTMASK(SDL_KEYUP), + SDL_KEYEVENTMASK = SDL_EVENTMASK(SDL_KEYDOWN)| + SDL_EVENTMASK(SDL_KEYUP), + SDL_MOUSEMOTIONMASK = SDL_EVENTMASK(SDL_MOUSEMOTION), + SDL_MOUSEBUTTONDOWNMASK = SDL_EVENTMASK(SDL_MOUSEBUTTONDOWN), + SDL_MOUSEBUTTONUPMASK = SDL_EVENTMASK(SDL_MOUSEBUTTONUP), + SDL_MOUSEEVENTMASK = SDL_EVENTMASK(SDL_MOUSEMOTION)| + SDL_EVENTMASK(SDL_MOUSEBUTTONDOWN)| + SDL_EVENTMASK(SDL_MOUSEBUTTONUP), + SDL_JOYAXISMOTIONMASK = SDL_EVENTMASK(SDL_JOYAXISMOTION), + SDL_JOYBALLMOTIONMASK = SDL_EVENTMASK(SDL_JOYBALLMOTION), + SDL_JOYHATMOTIONMASK = SDL_EVENTMASK(SDL_JOYHATMOTION), + SDL_JOYBUTTONDOWNMASK = SDL_EVENTMASK(SDL_JOYBUTTONDOWN), + SDL_JOYBUTTONUPMASK = SDL_EVENTMASK(SDL_JOYBUTTONUP), + SDL_JOYEVENTMASK = SDL_EVENTMASK(SDL_JOYAXISMOTION)| + SDL_EVENTMASK(SDL_JOYBALLMOTION)| + SDL_EVENTMASK(SDL_JOYHATMOTION)| + SDL_EVENTMASK(SDL_JOYBUTTONDOWN)| + SDL_EVENTMASK(SDL_JOYBUTTONUP), + SDL_VIDEORESIZEMASK = SDL_EVENTMASK(SDL_VIDEORESIZE), + SDL_VIDEOEXPOSEMASK = SDL_EVENTMASK(SDL_VIDEOEXPOSE), + SDL_QUITMASK = SDL_EVENTMASK(SDL_QUIT), + SDL_SYSWMEVENTMASK = SDL_EVENTMASK(SDL_SYSWMEVENT) +} SDL_EventMask ; +#define SDL_ALLEVENTS 0xFFFFFFFF + +/* Application visibility event structure */ +typedef struct SDL_ActiveEvent { + Uint8 type; /* SDL_ACTIVEEVENT */ + Uint8 gain; /* Whether given states were gained or lost (1/0) */ + Uint8 state; /* A mask of the focus states */ +} SDL_ActiveEvent; + +/* Keyboard event structure */ +typedef struct SDL_KeyboardEvent { + Uint8 type; /* SDL_KEYDOWN or SDL_KEYUP */ + Uint8 which; /* The keyboard device index */ + Uint8 state; /* SDL_PRESSED or SDL_RELEASED */ + SDL_keysym keysym; +} SDL_KeyboardEvent; + +/* Mouse motion event structure */ +typedef struct SDL_MouseMotionEvent { + Uint8 type; /* SDL_MOUSEMOTION */ + Uint8 which; /* The mouse device index */ + Uint8 state; /* The current button state */ + Uint16 x, y; /* The X/Y coordinates of the mouse */ + Sint16 xrel; /* The relative motion in the X direction */ + Sint16 yrel; /* The relative motion in the Y direction */ +} SDL_MouseMotionEvent; + +/* Mouse button event structure */ +typedef struct SDL_MouseButtonEvent { + Uint8 type; /* SDL_MOUSEBUTTONDOWN or SDL_MOUSEBUTTONUP */ + Uint8 which; /* The mouse device index */ + Uint8 button; /* The mouse button index */ + Uint8 state; /* SDL_PRESSED or SDL_RELEASED */ + Uint16 x, y; /* The X/Y coordinates of the mouse at press time */ +} SDL_MouseButtonEvent; + +/* Joystick axis motion event structure */ +typedef struct SDL_JoyAxisEvent { + Uint8 type; /* SDL_JOYAXISMOTION */ + Uint8 which; /* The joystick device index */ + Uint8 axis; /* The joystick axis index */ + Sint16 value; /* The axis value (range: -32768 to 32767) */ +} SDL_JoyAxisEvent; + +/* Joystick trackball motion event structure */ +typedef struct SDL_JoyBallEvent { + Uint8 type; /* SDL_JOYBALLMOTION */ + Uint8 which; /* The joystick device index */ + Uint8 ball; /* The joystick trackball index */ + Sint16 xrel; /* The relative motion in the X direction */ + Sint16 yrel; /* The relative motion in the Y direction */ +} SDL_JoyBallEvent; + +/* Joystick hat position change event structure */ +typedef struct SDL_JoyHatEvent { + Uint8 type; /* SDL_JOYHATMOTION */ + Uint8 which; /* The joystick device index */ + Uint8 hat; /* The joystick hat index */ + Uint8 value; /* The hat position value: + SDL_HAT_LEFTUP SDL_HAT_UP SDL_HAT_RIGHTUP + SDL_HAT_LEFT SDL_HAT_CENTERED SDL_HAT_RIGHT + SDL_HAT_LEFTDOWN SDL_HAT_DOWN SDL_HAT_RIGHTDOWN + Note that zero means the POV is centered. + */ +} SDL_JoyHatEvent; + +/* Joystick button event structure */ +typedef struct SDL_JoyButtonEvent { + Uint8 type; /* SDL_JOYBUTTONDOWN or SDL_JOYBUTTONUP */ + Uint8 which; /* The joystick device index */ + Uint8 button; /* The joystick button index */ + Uint8 state; /* SDL_PRESSED or SDL_RELEASED */ +} SDL_JoyButtonEvent; + +/* The "window resized" event + When you get this event, you are responsible for setting a new video + mode with the new width and height. + */ +typedef struct SDL_ResizeEvent { + Uint8 type; /* SDL_VIDEORESIZE */ + int w; /* New width */ + int h; /* New height */ +} SDL_ResizeEvent; + +/* The "screen redraw" event */ +typedef struct SDL_ExposeEvent { + Uint8 type; /* SDL_VIDEOEXPOSE */ +} SDL_ExposeEvent; + +/* The "quit requested" event */ +typedef struct SDL_QuitEvent { + Uint8 type; /* SDL_QUIT */ +} SDL_QuitEvent; + +/* A user-defined event type */ +typedef struct SDL_UserEvent { + Uint8 type; /* SDL_USEREVENT through SDL_NUMEVENTS-1 */ + int code; /* User defined event code */ + void *data1; /* User defined data pointer */ + void *data2; /* User defined data pointer */ +} SDL_UserEvent; + +/* If you want to use this event, you should include SDL_syswm.h */ +struct SDL_SysWMmsg; +typedef struct SDL_SysWMmsg SDL_SysWMmsg; +typedef struct SDL_SysWMEvent { + Uint8 type; + SDL_SysWMmsg *msg; +} SDL_SysWMEvent; + +/* General event structure */ +typedef union SDL_Event { + Uint8 type; + SDL_ActiveEvent active; + SDL_KeyboardEvent key; + SDL_MouseMotionEvent motion; + SDL_MouseButtonEvent button; + SDL_JoyAxisEvent jaxis; + SDL_JoyBallEvent jball; + SDL_JoyHatEvent jhat; + SDL_JoyButtonEvent jbutton; + SDL_ResizeEvent resize; + SDL_ExposeEvent expose; + SDL_QuitEvent quit; + SDL_UserEvent user; + SDL_SysWMEvent syswm; +} SDL_Event; + + +/* Function prototypes */ + +/* Pumps the event loop, gathering events from the input devices. + This function updates the event queue and internal input device state. + This should only be run in the thread that sets the video mode. +*/ +extern DECLSPEC void SDLCALL SDL_PumpEvents(void); + +/* Checks the event queue for messages and optionally returns them. + If 'action' is SDL_ADDEVENT, up to 'numevents' events will be added to + the back of the event queue. + If 'action' is SDL_PEEKEVENT, up to 'numevents' events at the front + of the event queue, matching 'mask', will be returned and will not + be removed from the queue. + If 'action' is SDL_GETEVENT, up to 'numevents' events at the front + of the event queue, matching 'mask', will be returned and will be + removed from the queue. + This function returns the number of events actually stored, or -1 + if there was an error. This function is thread-safe. +*/ +typedef enum { + SDL_ADDEVENT, + SDL_PEEKEVENT, + SDL_GETEVENT +} SDL_eventaction; +/* */ +extern DECLSPEC int SDLCALL SDL_PeepEvents(SDL_Event *events, int numevents, + SDL_eventaction action, Uint32 mask); + +/* Polls for currently pending events, and returns 1 if there are any pending + events, or 0 if there are none available. If 'event' is not NULL, the next + event is removed from the queue and stored in that area. + */ +extern DECLSPEC int SDLCALL SDL_PollEvent(SDL_Event *event); + +/* Waits indefinitely for the next available event, returning 1, or 0 if there + was an error while waiting for events. If 'event' is not NULL, the next + event is removed from the queue and stored in that area. + */ +extern DECLSPEC int SDLCALL SDL_WaitEvent(SDL_Event *event); + +/* Add an event to the event queue. + This function returns 0 on success, or -1 if the event queue was full + or there was some other error. + */ +extern DECLSPEC int SDLCALL SDL_PushEvent(SDL_Event *event); + +/* + This function sets up a filter to process all events before they + change internal state and are posted to the internal event queue. + + The filter is protypted as: +*/ +typedef int (SDLCALL *SDL_EventFilter)(const SDL_Event *event); +/* + If the filter returns 1, then the event will be added to the internal queue. + If it returns 0, then the event will be dropped from the queue, but the + internal state will still be updated. This allows selective filtering of + dynamically arriving events. + + WARNING: Be very careful of what you do in the event filter function, as + it may run in a different thread! + + There is one caveat when dealing with the SDL_QUITEVENT event type. The + event filter is only called when the window manager desires to close the + application window. If the event filter returns 1, then the window will + be closed, otherwise the window will remain open if possible. + If the quit event is generated by an interrupt signal, it will bypass the + internal queue and be delivered to the application at the next event poll. +*/ +extern DECLSPEC void SDLCALL SDL_SetEventFilter(SDL_EventFilter filter); + +/* + Return the current event filter - can be used to "chain" filters. + If there is no event filter set, this function returns NULL. +*/ +extern DECLSPEC SDL_EventFilter SDLCALL SDL_GetEventFilter(void); + +/* + This function allows you to set the state of processing certain events. + If 'state' is set to SDL_IGNORE, that event will be automatically dropped + from the event queue and will not event be filtered. + If 'state' is set to SDL_ENABLE, that event will be processed normally. + If 'state' is set to SDL_QUERY, SDL_EventState() will return the + current processing state of the specified event. +*/ +#define SDL_QUERY -1 +#define SDL_IGNORE 0 +#define SDL_DISABLE 0 +#define SDL_ENABLE 1 +extern DECLSPEC Uint8 SDLCALL SDL_EventState(Uint8 type, int state); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_events_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_getenv.h b/reaction/engine/code/SDL12/include/SDL_getenv.h new file mode 100644 index 00000000..853b9ce4 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_getenv.h @@ -0,0 +1,24 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* DEPRECATED */ +#include "SDL_stdinc.h" diff --git a/reaction/engine/code/SDL12/include/SDL_joystick.h b/reaction/engine/code/SDL12/include/SDL_joystick.h new file mode 100644 index 00000000..e4f72f1a --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_joystick.h @@ -0,0 +1,167 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* Include file for SDL joystick event handling */ + +#ifndef _SDL_joystick_h +#define _SDL_joystick_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* In order to use these functions, SDL_Init() must have been called + with the SDL_INIT_JOYSTICK flag. This causes SDL to scan the system + for joysticks, and load appropriate drivers. +*/ + +/* The joystick structure used to identify an SDL joystick */ +struct _SDL_Joystick; +typedef struct _SDL_Joystick SDL_Joystick; + + +/* Function prototypes */ +/* + * Count the number of joysticks attached to the system + */ +extern DECLSPEC int SDLCALL SDL_NumJoysticks(void); + +/* + * Get the implementation dependent name of a joystick. + * This can be called before any joysticks are opened. + * If no name can be found, this function returns NULL. + */ +extern DECLSPEC const char * SDLCALL SDL_JoystickName(int device_index); + +/* + * Open a joystick for use - the index passed as an argument refers to + * the N'th joystick on the system. This index is the value which will + * identify this joystick in future joystick events. + * + * This function returns a joystick identifier, or NULL if an error occurred. + */ +extern DECLSPEC SDL_Joystick * SDLCALL SDL_JoystickOpen(int device_index); + +/* + * Returns 1 if the joystick has been opened, or 0 if it has not. + */ +extern DECLSPEC int SDLCALL SDL_JoystickOpened(int device_index); + +/* + * Get the device index of an opened joystick. + */ +extern DECLSPEC int SDLCALL SDL_JoystickIndex(SDL_Joystick *joystick); + +/* + * Get the number of general axis controls on a joystick + */ +extern DECLSPEC int SDLCALL SDL_JoystickNumAxes(SDL_Joystick *joystick); + +/* + * Get the number of trackballs on a joystick + * Joystick trackballs have only relative motion events associated + * with them and their state cannot be polled. + */ +extern DECLSPEC int SDLCALL SDL_JoystickNumBalls(SDL_Joystick *joystick); + +/* + * Get the number of POV hats on a joystick + */ +extern DECLSPEC int SDLCALL SDL_JoystickNumHats(SDL_Joystick *joystick); + +/* + * Get the number of buttons on a joystick + */ +extern DECLSPEC int SDLCALL SDL_JoystickNumButtons(SDL_Joystick *joystick); + +/* + * Update the current state of the open joysticks. + * This is called automatically by the event loop if any joystick + * events are enabled. + */ +extern DECLSPEC void SDLCALL SDL_JoystickUpdate(void); + +/* + * Enable/disable joystick event polling. + * If joystick events are disabled, you must call SDL_JoystickUpdate() + * yourself and check the state of the joystick when you want joystick + * information. + * The state can be one of SDL_QUERY, SDL_ENABLE or SDL_IGNORE. + */ +extern DECLSPEC int SDLCALL SDL_JoystickEventState(int state); + +/* + * Get the current state of an axis control on a joystick + * The state is a value ranging from -32768 to 32767. + * The axis indices start at index 0. + */ +extern DECLSPEC Sint16 SDLCALL SDL_JoystickGetAxis(SDL_Joystick *joystick, int axis); + +/* + * Get the current state of a POV hat on a joystick + * The return value is one of the following positions: + */ +#define SDL_HAT_CENTERED 0x00 +#define SDL_HAT_UP 0x01 +#define SDL_HAT_RIGHT 0x02 +#define SDL_HAT_DOWN 0x04 +#define SDL_HAT_LEFT 0x08 +#define SDL_HAT_RIGHTUP (SDL_HAT_RIGHT|SDL_HAT_UP) +#define SDL_HAT_RIGHTDOWN (SDL_HAT_RIGHT|SDL_HAT_DOWN) +#define SDL_HAT_LEFTUP (SDL_HAT_LEFT|SDL_HAT_UP) +#define SDL_HAT_LEFTDOWN (SDL_HAT_LEFT|SDL_HAT_DOWN) +/* + * The hat indices start at index 0. + */ +extern DECLSPEC Uint8 SDLCALL SDL_JoystickGetHat(SDL_Joystick *joystick, int hat); + +/* + * Get the ball axis change since the last poll + * This returns 0, or -1 if you passed it invalid parameters. + * The ball indices start at index 0. + */ +extern DECLSPEC int SDLCALL SDL_JoystickGetBall(SDL_Joystick *joystick, int ball, int *dx, int *dy); + +/* + * Get the current state of a button on a joystick + * The button indices start at index 0. + */ +extern DECLSPEC Uint8 SDLCALL SDL_JoystickGetButton(SDL_Joystick *joystick, int button); + +/* + * Close a joystick previously opened with SDL_JoystickOpen() + */ +extern DECLSPEC void SDLCALL SDL_JoystickClose(SDL_Joystick *joystick); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_joystick_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_keyboard.h b/reaction/engine/code/SDL12/include/SDL_keyboard.h new file mode 100644 index 00000000..1ad7dcaa --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_keyboard.h @@ -0,0 +1,121 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* Include file for SDL keyboard event handling */ + +#ifndef _SDL_keyboard_h +#define _SDL_keyboard_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_keysym.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Keysym structure + - The scancode is hardware dependent, and should not be used by general + applications. If no hardware scancode is available, it will be 0. + + - The 'unicode' translated character is only available when character + translation is enabled by the SDL_EnableUNICODE() API. If non-zero, + this is a UNICODE character corresponding to the keypress. If the + high 9 bits of the character are 0, then this maps to the equivalent + ASCII character: + char ch; + if ( (keysym.unicode & 0xFF80) == 0 ) { + ch = keysym.unicode & 0x7F; + } else { + An international character.. + } + */ +typedef struct SDL_keysym { + Uint8 scancode; /* hardware specific scancode */ + SDLKey sym; /* SDL virtual keysym */ + SDLMod mod; /* current key modifiers */ + Uint16 unicode; /* translated character */ +} SDL_keysym; + +/* This is the mask which refers to all hotkey bindings */ +#define SDL_ALL_HOTKEYS 0xFFFFFFFF + +/* Function prototypes */ +/* + * Enable/Disable UNICODE translation of keyboard input. + * This translation has some overhead, so translation defaults off. + * If 'enable' is 1, translation is enabled. + * If 'enable' is 0, translation is disabled. + * If 'enable' is -1, the translation state is not changed. + * It returns the previous state of keyboard translation. + */ +extern DECLSPEC int SDLCALL SDL_EnableUNICODE(int enable); + +/* + * Enable/Disable keyboard repeat. Keyboard repeat defaults to off. + * 'delay' is the initial delay in ms between the time when a key is + * pressed, and keyboard repeat begins. + * 'interval' is the time in ms between keyboard repeat events. + */ +#define SDL_DEFAULT_REPEAT_DELAY 500 +#define SDL_DEFAULT_REPEAT_INTERVAL 30 +/* + * If 'delay' is set to 0, keyboard repeat is disabled. + */ +extern DECLSPEC int SDLCALL SDL_EnableKeyRepeat(int delay, int interval); +extern DECLSPEC void SDLCALL SDL_GetKeyRepeat(int *delay, int *interval); + +/* + * Get a snapshot of the current state of the keyboard. + * Returns an array of keystates, indexed by the SDLK_* syms. + * Used: + * Uint8 *keystate = SDL_GetKeyState(NULL); + * if ( keystate[SDLK_RETURN] ) ... is pressed. + */ +extern DECLSPEC Uint8 * SDLCALL SDL_GetKeyState(int *numkeys); + +/* + * Get the current key modifier state + */ +extern DECLSPEC SDLMod SDLCALL SDL_GetModState(void); + +/* + * Set the current key modifier state + * This does not change the keyboard state, only the key modifier flags. + */ +extern DECLSPEC void SDLCALL SDL_SetModState(SDLMod modstate); + +/* + * Get the name of an SDL virtual keysym + */ +extern DECLSPEC char * SDLCALL SDL_GetKeyName(SDLKey key); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_keyboard_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_keysym.h b/reaction/engine/code/SDL12/include/SDL_keysym.h new file mode 100644 index 00000000..ff44a035 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_keysym.h @@ -0,0 +1,311 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifndef _SDL_keysym_h +#define _SDL_keysym_h + +/* What we really want is a mapping of every raw key on the keyboard. + To support international keyboards, we use the range 0xA1 - 0xFF + as international virtual keycodes. We'll follow in the footsteps of X11... + The names of the keys + */ + +typedef enum { + /* The keyboard syms have been cleverly chosen to map to ASCII */ + SDLK_UNKNOWN = 0, + SDLK_FIRST = 0, + SDLK_BACKSPACE = 8, + SDLK_TAB = 9, + SDLK_CLEAR = 12, + SDLK_RETURN = 13, + SDLK_PAUSE = 19, + SDLK_ESCAPE = 27, + SDLK_SPACE = 32, + SDLK_EXCLAIM = 33, + SDLK_QUOTEDBL = 34, + SDLK_HASH = 35, + SDLK_DOLLAR = 36, + SDLK_AMPERSAND = 38, + SDLK_QUOTE = 39, + SDLK_LEFTPAREN = 40, + SDLK_RIGHTPAREN = 41, + SDLK_ASTERISK = 42, + SDLK_PLUS = 43, + SDLK_COMMA = 44, + SDLK_MINUS = 45, + SDLK_PERIOD = 46, + SDLK_SLASH = 47, + SDLK_0 = 48, + SDLK_1 = 49, + SDLK_2 = 50, + SDLK_3 = 51, + SDLK_4 = 52, + SDLK_5 = 53, + SDLK_6 = 54, + SDLK_7 = 55, + SDLK_8 = 56, + SDLK_9 = 57, + SDLK_COLON = 58, + SDLK_SEMICOLON = 59, + SDLK_LESS = 60, + SDLK_EQUALS = 61, + SDLK_GREATER = 62, + SDLK_QUESTION = 63, + SDLK_AT = 64, + /* + Skip uppercase letters + */ + SDLK_LEFTBRACKET = 91, + SDLK_BACKSLASH = 92, + SDLK_RIGHTBRACKET = 93, + SDLK_CARET = 94, + SDLK_UNDERSCORE = 95, + SDLK_BACKQUOTE = 96, + SDLK_a = 97, + SDLK_b = 98, + SDLK_c = 99, + SDLK_d = 100, + SDLK_e = 101, + SDLK_f = 102, + SDLK_g = 103, + SDLK_h = 104, + SDLK_i = 105, + SDLK_j = 106, + SDLK_k = 107, + SDLK_l = 108, + SDLK_m = 109, + SDLK_n = 110, + SDLK_o = 111, + SDLK_p = 112, + SDLK_q = 113, + SDLK_r = 114, + SDLK_s = 115, + SDLK_t = 116, + SDLK_u = 117, + SDLK_v = 118, + SDLK_w = 119, + SDLK_x = 120, + SDLK_y = 121, + SDLK_z = 122, + SDLK_DELETE = 127, + /* End of ASCII mapped keysyms */ + + /* International keyboard syms */ + SDLK_WORLD_0 = 160, /* 0xA0 */ + SDLK_WORLD_1 = 161, + SDLK_WORLD_2 = 162, + SDLK_WORLD_3 = 163, + SDLK_WORLD_4 = 164, + SDLK_WORLD_5 = 165, + SDLK_WORLD_6 = 166, + SDLK_WORLD_7 = 167, + SDLK_WORLD_8 = 168, + SDLK_WORLD_9 = 169, + SDLK_WORLD_10 = 170, + SDLK_WORLD_11 = 171, + SDLK_WORLD_12 = 172, + SDLK_WORLD_13 = 173, + SDLK_WORLD_14 = 174, + SDLK_WORLD_15 = 175, + SDLK_WORLD_16 = 176, + SDLK_WORLD_17 = 177, + SDLK_WORLD_18 = 178, + SDLK_WORLD_19 = 179, + SDLK_WORLD_20 = 180, + SDLK_WORLD_21 = 181, + SDLK_WORLD_22 = 182, + SDLK_WORLD_23 = 183, + SDLK_WORLD_24 = 184, + SDLK_WORLD_25 = 185, + SDLK_WORLD_26 = 186, + SDLK_WORLD_27 = 187, + SDLK_WORLD_28 = 188, + SDLK_WORLD_29 = 189, + SDLK_WORLD_30 = 190, + SDLK_WORLD_31 = 191, + SDLK_WORLD_32 = 192, + SDLK_WORLD_33 = 193, + SDLK_WORLD_34 = 194, + SDLK_WORLD_35 = 195, + SDLK_WORLD_36 = 196, + SDLK_WORLD_37 = 197, + SDLK_WORLD_38 = 198, + SDLK_WORLD_39 = 199, + SDLK_WORLD_40 = 200, + SDLK_WORLD_41 = 201, + SDLK_WORLD_42 = 202, + SDLK_WORLD_43 = 203, + SDLK_WORLD_44 = 204, + SDLK_WORLD_45 = 205, + SDLK_WORLD_46 = 206, + SDLK_WORLD_47 = 207, + SDLK_WORLD_48 = 208, + SDLK_WORLD_49 = 209, + SDLK_WORLD_50 = 210, + SDLK_WORLD_51 = 211, + SDLK_WORLD_52 = 212, + SDLK_WORLD_53 = 213, + SDLK_WORLD_54 = 214, + SDLK_WORLD_55 = 215, + SDLK_WORLD_56 = 216, + SDLK_WORLD_57 = 217, + SDLK_WORLD_58 = 218, + SDLK_WORLD_59 = 219, + SDLK_WORLD_60 = 220, + SDLK_WORLD_61 = 221, + SDLK_WORLD_62 = 222, + SDLK_WORLD_63 = 223, + SDLK_WORLD_64 = 224, + SDLK_WORLD_65 = 225, + SDLK_WORLD_66 = 226, + SDLK_WORLD_67 = 227, + SDLK_WORLD_68 = 228, + SDLK_WORLD_69 = 229, + SDLK_WORLD_70 = 230, + SDLK_WORLD_71 = 231, + SDLK_WORLD_72 = 232, + SDLK_WORLD_73 = 233, + SDLK_WORLD_74 = 234, + SDLK_WORLD_75 = 235, + SDLK_WORLD_76 = 236, + SDLK_WORLD_77 = 237, + SDLK_WORLD_78 = 238, + SDLK_WORLD_79 = 239, + SDLK_WORLD_80 = 240, + SDLK_WORLD_81 = 241, + SDLK_WORLD_82 = 242, + SDLK_WORLD_83 = 243, + SDLK_WORLD_84 = 244, + SDLK_WORLD_85 = 245, + SDLK_WORLD_86 = 246, + SDLK_WORLD_87 = 247, + SDLK_WORLD_88 = 248, + SDLK_WORLD_89 = 249, + SDLK_WORLD_90 = 250, + SDLK_WORLD_91 = 251, + SDLK_WORLD_92 = 252, + SDLK_WORLD_93 = 253, + SDLK_WORLD_94 = 254, + SDLK_WORLD_95 = 255, /* 0xFF */ + + /* Numeric keypad */ + SDLK_KP0 = 256, + SDLK_KP1 = 257, + SDLK_KP2 = 258, + SDLK_KP3 = 259, + SDLK_KP4 = 260, + SDLK_KP5 = 261, + SDLK_KP6 = 262, + SDLK_KP7 = 263, + SDLK_KP8 = 264, + SDLK_KP9 = 265, + SDLK_KP_PERIOD = 266, + SDLK_KP_DIVIDE = 267, + SDLK_KP_MULTIPLY = 268, + SDLK_KP_MINUS = 269, + SDLK_KP_PLUS = 270, + SDLK_KP_ENTER = 271, + SDLK_KP_EQUALS = 272, + + /* Arrows + Home/End pad */ + SDLK_UP = 273, + SDLK_DOWN = 274, + SDLK_RIGHT = 275, + SDLK_LEFT = 276, + SDLK_INSERT = 277, + SDLK_HOME = 278, + SDLK_END = 279, + SDLK_PAGEUP = 280, + SDLK_PAGEDOWN = 281, + + /* Function keys */ + SDLK_F1 = 282, + SDLK_F2 = 283, + SDLK_F3 = 284, + SDLK_F4 = 285, + SDLK_F5 = 286, + SDLK_F6 = 287, + SDLK_F7 = 288, + SDLK_F8 = 289, + SDLK_F9 = 290, + SDLK_F10 = 291, + SDLK_F11 = 292, + SDLK_F12 = 293, + SDLK_F13 = 294, + SDLK_F14 = 295, + SDLK_F15 = 296, + + /* Key state modifier keys */ + SDLK_NUMLOCK = 300, + SDLK_CAPSLOCK = 301, + SDLK_SCROLLOCK = 302, + SDLK_RSHIFT = 303, + SDLK_LSHIFT = 304, + SDLK_RCTRL = 305, + SDLK_LCTRL = 306, + SDLK_RALT = 307, + SDLK_LALT = 308, + SDLK_RMETA = 309, + SDLK_LMETA = 310, + SDLK_LSUPER = 311, /* Left "Windows" key */ + SDLK_RSUPER = 312, /* Right "Windows" key */ + SDLK_MODE = 313, /* "Alt Gr" key */ + SDLK_COMPOSE = 314, /* Multi-key compose key */ + + /* Miscellaneous function keys */ + SDLK_HELP = 315, + SDLK_PRINT = 316, + SDLK_SYSREQ = 317, + SDLK_BREAK = 318, + SDLK_MENU = 319, + SDLK_POWER = 320, /* Power Macintosh power key */ + SDLK_EURO = 321, /* Some european keyboards */ + SDLK_UNDO = 322, /* Atari keyboard has Undo */ + + /* Add any other keys here */ + + SDLK_LAST +} SDLKey; + +/* Enumeration of valid key mods (possibly OR'd together) */ +typedef enum { + KMOD_NONE = 0x0000, + KMOD_LSHIFT= 0x0001, + KMOD_RSHIFT= 0x0002, + KMOD_LCTRL = 0x0040, + KMOD_RCTRL = 0x0080, + KMOD_LALT = 0x0100, + KMOD_RALT = 0x0200, + KMOD_LMETA = 0x0400, + KMOD_RMETA = 0x0800, + KMOD_NUM = 0x1000, + KMOD_CAPS = 0x2000, + KMOD_MODE = 0x4000, + KMOD_RESERVED = 0x8000 +} SDLMod; + +#define KMOD_CTRL (KMOD_LCTRL|KMOD_RCTRL) +#define KMOD_SHIFT (KMOD_LSHIFT|KMOD_RSHIFT) +#define KMOD_ALT (KMOD_LALT|KMOD_RALT) +#define KMOD_META (KMOD_LMETA|KMOD_RMETA) + +#endif /* _SDL_keysym_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_loadso.h b/reaction/engine/code/SDL12/include/SDL_loadso.h new file mode 100644 index 00000000..ce964494 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_loadso.h @@ -0,0 +1,74 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* System dependent library loading routines */ + +/* Some things to keep in mind: + - These functions only work on C function names. Other languages may + have name mangling and intrinsic language support that varies from + compiler to compiler. + - Make sure you declare your function pointers with the same calling + convention as the actual library function. Your code will crash + mysteriously if you do not do this. + - Avoid namespace collisions. If you load a symbol from the library, + it is not defined whether or not it goes into the global symbol + namespace for the application. If it does and it conflicts with + symbols in your code or other shared libraries, you will not get + the results you expect. :) +*/ + + +#ifndef _SDL_loadso_h +#define _SDL_loadso_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* This function dynamically loads a shared object and returns a pointer + * to the object handle (or NULL if there was an error). + * The 'sofile' parameter is a system dependent name of the object file. + */ +extern DECLSPEC void * SDLCALL SDL_LoadObject(const char *sofile); + +/* Given an object handle, this function looks up the address of the + * named function in the shared object and returns it. This address + * is no longer valid after calling SDL_UnloadObject(). + */ +extern DECLSPEC void * SDLCALL SDL_LoadFunction(void *handle, const char *name); + +/* Unload a shared object from memory */ +extern DECLSPEC void SDLCALL SDL_UnloadObject(void *handle); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_loadso_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_main.h b/reaction/engine/code/SDL12/include/SDL_main.h new file mode 100644 index 00000000..cf8b728d --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_main.h @@ -0,0 +1,98 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifndef _SDL_main_h +#define _SDL_main_h + +#include "SDL_stdinc.h" + +/* Redefine main() on Win32 and MacOS so that it is called by winmain.c */ + +#if defined(__WIN32__) || \ + (defined(__MWERKS__) && !defined(__BEOS__)) || \ + defined(__MACOS__) || defined(__MACOSX__) || \ + defined(__SYMBIAN32__) || defined(QWS) + +#ifdef __cplusplus +#define C_LINKAGE "C" +#else +#define C_LINKAGE +#endif /* __cplusplus */ + +/* The application's main() function must be called with C linkage, + and should be declared like this: +#ifdef __cplusplus +extern "C" +#endif + int main(int argc, char *argv[]) + { + } + */ +#define main SDL_main + +/* The prototype for the application's main() function */ +extern C_LINKAGE int SDL_main(int argc, char *argv[]); + + +/* From the SDL library code -- needed for registering the app on Win32 */ +#ifdef __WIN32__ + +#include "begin_code.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* This should be called from your WinMain() function, if any */ +extern DECLSPEC void SDLCALL SDL_SetModuleHandle(void *hInst); +/* This can also be called, but is no longer necessary */ +extern DECLSPEC int SDLCALL SDL_RegisterApp(char *name, Uint32 style, void *hInst); +/* This can also be called, but is no longer necessary (SDL_Quit calls it) */ +extern DECLSPEC void SDLCALL SDL_UnregisterApp(void); +#ifdef __cplusplus +} +#endif +#include "close_code.h" +#endif + +/* From the SDL library code -- needed for registering QuickDraw on MacOS */ +#if defined(__MACOS__) + +#include "begin_code.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declaration so we don't need to include QuickDraw.h */ +struct QDGlobals; + +/* This should be called from your main() function, if any */ +extern DECLSPEC void SDLCALL SDL_InitQuickDraw(struct QDGlobals *the_qd); + +#ifdef __cplusplus +} +#endif +#include "close_code.h" +#endif + +#endif /* Need to redefine main()? */ + +#endif /* _SDL_main_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_mouse.h b/reaction/engine/code/SDL12/include/SDL_mouse.h new file mode 100644 index 00000000..c2364d85 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_mouse.h @@ -0,0 +1,136 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* Include file for SDL mouse event handling */ + +#ifndef _SDL_mouse_h +#define _SDL_mouse_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_video.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct WMcursor WMcursor; /* Implementation dependent */ +typedef struct SDL_Cursor { + SDL_Rect area; /* The area of the mouse cursor */ + Sint16 hot_x, hot_y; /* The "tip" of the cursor */ + Uint8 *data; /* B/W cursor data */ + Uint8 *mask; /* B/W cursor mask */ + Uint8 *save[2]; /* Place to save cursor area */ + WMcursor *wm_cursor; /* Window-manager cursor */ +} SDL_Cursor; + +/* Function prototypes */ +/* + * Retrieve the current state of the mouse. + * The current button state is returned as a button bitmask, which can + * be tested using the SDL_BUTTON(X) macros, and x and y are set to the + * current mouse cursor position. You can pass NULL for either x or y. + */ +extern DECLSPEC Uint8 SDLCALL SDL_GetMouseState(int *x, int *y); + +/* + * Retrieve the current state of the mouse. + * The current button state is returned as a button bitmask, which can + * be tested using the SDL_BUTTON(X) macros, and x and y are set to the + * mouse deltas since the last call to SDL_GetRelativeMouseState(). + */ +extern DECLSPEC Uint8 SDLCALL SDL_GetRelativeMouseState(int *x, int *y); + +/* + * Set the position of the mouse cursor (generates a mouse motion event) + */ +extern DECLSPEC void SDLCALL SDL_WarpMouse(Uint16 x, Uint16 y); + +/* + * Create a cursor using the specified data and mask (in MSB format). + * The cursor width must be a multiple of 8 bits. + * + * The cursor is created in black and white according to the following: + * data mask resulting pixel on screen + * 0 1 White + * 1 1 Black + * 0 0 Transparent + * 1 0 Inverted color if possible, black if not. + * + * Cursors created with this function must be freed with SDL_FreeCursor(). + */ +extern DECLSPEC SDL_Cursor * SDLCALL SDL_CreateCursor + (Uint8 *data, Uint8 *mask, int w, int h, int hot_x, int hot_y); + +/* + * Set the currently active cursor to the specified one. + * If the cursor is currently visible, the change will be immediately + * represented on the display. + */ +extern DECLSPEC void SDLCALL SDL_SetCursor(SDL_Cursor *cursor); + +/* + * Returns the currently active cursor. + */ +extern DECLSPEC SDL_Cursor * SDLCALL SDL_GetCursor(void); + +/* + * Deallocates a cursor created with SDL_CreateCursor(). + */ +extern DECLSPEC void SDLCALL SDL_FreeCursor(SDL_Cursor *cursor); + +/* + * Toggle whether or not the cursor is shown on the screen. + * The cursor start off displayed, but can be turned off. + * SDL_ShowCursor() returns 1 if the cursor was being displayed + * before the call, or 0 if it was not. You can query the current + * state by passing a 'toggle' value of -1. + */ +extern DECLSPEC int SDLCALL SDL_ShowCursor(int toggle); + +/* Used as a mask when testing buttons in buttonstate + Button 1: Left mouse button + Button 2: Middle mouse button + Button 3: Right mouse button + Button 4: Mouse wheel up (may also be a real button) + Button 5: Mouse wheel down (may also be a real button) + */ +#define SDL_BUTTON(X) (1 << ((X)-1)) +#define SDL_BUTTON_LEFT 1 +#define SDL_BUTTON_MIDDLE 2 +#define SDL_BUTTON_RIGHT 3 +#define SDL_BUTTON_WHEELUP 4 +#define SDL_BUTTON_WHEELDOWN 5 +#define SDL_BUTTON_LMASK SDL_BUTTON(SDL_BUTTON_LEFT) +#define SDL_BUTTON_MMASK SDL_BUTTON(SDL_BUTTON_MIDDLE) +#define SDL_BUTTON_RMASK SDL_BUTTON(SDL_BUTTON_RIGHT) + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_mouse_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_mutex.h b/reaction/engine/code/SDL12/include/SDL_mutex.h new file mode 100644 index 00000000..00165281 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_mutex.h @@ -0,0 +1,162 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifndef _SDL_mutex_h +#define _SDL_mutex_h + +/* Functions to provide thread synchronization primitives + + These are independent of the other SDL routines. +*/ + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Synchronization functions which can time out return this value + if they time out. +*/ +#define SDL_MUTEX_TIMEDOUT 1 + +/* This is the timeout value which corresponds to never time out */ +#define SDL_MUTEX_MAXWAIT (~(Uint32)0) + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Mutex functions */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* The SDL mutex structure, defined in SDL_mutex.c */ +struct SDL_mutex; +typedef struct SDL_mutex SDL_mutex; + +/* Create a mutex, initialized unlocked */ +extern DECLSPEC SDL_mutex * SDLCALL SDL_CreateMutex(void); + +/* Lock the mutex (Returns 0, or -1 on error) */ +#define SDL_LockMutex(m) SDL_mutexP(m) +extern DECLSPEC int SDLCALL SDL_mutexP(SDL_mutex *mutex); + +/* Unlock the mutex (Returns 0, or -1 on error) + It is an error to unlock a mutex that has not been locked by + the current thread, and doing so results in undefined behavior. + */ +#define SDL_UnlockMutex(m) SDL_mutexV(m) +extern DECLSPEC int SDLCALL SDL_mutexV(SDL_mutex *mutex); + +/* Destroy a mutex */ +extern DECLSPEC void SDLCALL SDL_DestroyMutex(SDL_mutex *mutex); + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Semaphore functions */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* The SDL semaphore structure, defined in SDL_sem.c */ +struct SDL_semaphore; +typedef struct SDL_semaphore SDL_sem; + +/* Create a semaphore, initialized with value, returns NULL on failure. */ +extern DECLSPEC SDL_sem * SDLCALL SDL_CreateSemaphore(Uint32 initial_value); + +/* Destroy a semaphore */ +extern DECLSPEC void SDLCALL SDL_DestroySemaphore(SDL_sem *sem); + +/* This function suspends the calling thread until the semaphore pointed + * to by sem has a positive count. It then atomically decreases the semaphore + * count. + */ +extern DECLSPEC int SDLCALL SDL_SemWait(SDL_sem *sem); + +/* Non-blocking variant of SDL_SemWait(), returns 0 if the wait succeeds, + SDL_MUTEX_TIMEDOUT if the wait would block, and -1 on error. +*/ +extern DECLSPEC int SDLCALL SDL_SemTryWait(SDL_sem *sem); + +/* Variant of SDL_SemWait() with a timeout in milliseconds, returns 0 if + the wait succeeds, SDL_MUTEX_TIMEDOUT if the wait does not succeed in + the allotted time, and -1 on error. + On some platforms this function is implemented by looping with a delay + of 1 ms, and so should be avoided if possible. +*/ +extern DECLSPEC int SDLCALL SDL_SemWaitTimeout(SDL_sem *sem, Uint32 ms); + +/* Atomically increases the semaphore's count (not blocking), returns 0, + or -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_SemPost(SDL_sem *sem); + +/* Returns the current count of the semaphore */ +extern DECLSPEC Uint32 SDLCALL SDL_SemValue(SDL_sem *sem); + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Condition variable functions */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* The SDL condition variable structure, defined in SDL_cond.c */ +struct SDL_cond; +typedef struct SDL_cond SDL_cond; + +/* Create a condition variable */ +extern DECLSPEC SDL_cond * SDLCALL SDL_CreateCond(void); + +/* Destroy a condition variable */ +extern DECLSPEC void SDLCALL SDL_DestroyCond(SDL_cond *cond); + +/* Restart one of the threads that are waiting on the condition variable, + returns 0 or -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_CondSignal(SDL_cond *cond); + +/* Restart all threads that are waiting on the condition variable, + returns 0 or -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_CondBroadcast(SDL_cond *cond); + +/* Wait on the condition variable, unlocking the provided mutex. + The mutex must be locked before entering this function! + The mutex is re-locked once the condition variable is signaled. + Returns 0 when it is signaled, or -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_CondWait(SDL_cond *cond, SDL_mutex *mut); + +/* Waits for at most 'ms' milliseconds, and returns 0 if the condition + variable is signaled, SDL_MUTEX_TIMEDOUT if the condition is not + signaled in the allotted time, and -1 on error. + On some platforms this function is implemented by looping with a delay + of 1 ms, and so should be avoided if possible. +*/ +extern DECLSPEC int SDLCALL SDL_CondWaitTimeout(SDL_cond *cond, SDL_mutex *mutex, Uint32 ms); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_mutex_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_name.h b/reaction/engine/code/SDL12/include/SDL_name.h new file mode 100644 index 00000000..511619af --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_name.h @@ -0,0 +1,11 @@ + +#ifndef _SDLname_h_ +#define _SDLname_h_ + +#if defined(__STDC__) || defined(__cplusplus) +#define NeedFunctionPrototypes 1 +#endif + +#define SDL_NAME(X) SDL_##X + +#endif /* _SDLname_h_ */ diff --git a/reaction/engine/code/SDL12/include/SDL_opengl.h b/reaction/engine/code/SDL12/include/SDL_opengl.h new file mode 100644 index 00000000..36c0a309 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_opengl.h @@ -0,0 +1,6551 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* This is a simple file to encapsulate the OpenGL API headers */ + +#include "SDL_config.h" + +#ifdef __WIN32__ +#define WIN32_LEAN_AND_MEAN +#ifndef NOMINMAX +#define NOMINMAX /* Don't defined min() and max() */ +#endif +#include +#endif +#ifndef NO_SDL_GLEXT +#define __glext_h_ /* Don't let gl.h include glext.h */ +#endif +#if defined(__MACOSX__) +#include /* Header File For The OpenGL Library */ +#include /* Header File For The GLU Library */ +#elif defined(__MACOS__) +#include /* Header File For The OpenGL Library */ +#include /* Header File For The GLU Library */ +#else +#include /* Header File For The OpenGL Library */ +#include /* Header File For The GLU Library */ +#endif +#ifndef NO_SDL_GLEXT +#undef __glext_h_ +#endif + +/* This file taken from "GLext.h" from the Jeff Molofee OpenGL tutorials. + It is included here because glext.h is not available on some systems. + If you don't want this version included, simply define "NO_SDL_GLEXT" + */ +#ifndef NO_SDL_GLEXT +#if !defined(__glext_h_) && !defined(GL_GLEXT_LEGACY) +#define __glext_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2004 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: This software was created using the +** OpenGL(R) version 1.2.1 Sample Implementation published by SGI, but has +** not been independently verified as being compliant with the OpenGL(R) +** version 1.2.1 Specification. +*/ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#define WIN32_LEAN_AND_MEAN 1 +#include +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif +#ifndef GLAPI +#define GLAPI extern +#endif + +/*************************************************************/ + +/* Header file version number, required by OpenGL ABI for Linux */ +/* glext.h last updated 2005/06/20 */ +/* Current version at http://oss.sgi.com/projects/ogl-sample/registry/ */ +#define GL_GLEXT_VERSION 29 + +#ifndef GL_VERSION_1_2 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_RESCALE_NORMAL 0x803A +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 +#define GL_SINGLE_COLOR 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR 0x81FA +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#endif + +#ifndef GL_ARB_imaging +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_BLEND_COLOR 0x8005 +#define GL_FUNC_ADD 0x8006 +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +#define GL_BLEND_EQUATION 0x8009 +#define GL_FUNC_SUBTRACT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_CONVOLUTION_1D 0x8010 +#define GL_CONVOLUTION_2D 0x8011 +#define GL_SEPARABLE_2D 0x8012 +#define GL_CONVOLUTION_BORDER_MODE 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS 0x8015 +#define GL_REDUCE 0x8016 +#define GL_CONVOLUTION_FORMAT 0x8017 +#define GL_CONVOLUTION_WIDTH 0x8018 +#define GL_CONVOLUTION_HEIGHT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023 +#define GL_HISTOGRAM 0x8024 +#define GL_PROXY_HISTOGRAM 0x8025 +#define GL_HISTOGRAM_WIDTH 0x8026 +#define GL_HISTOGRAM_FORMAT 0x8027 +#define GL_HISTOGRAM_RED_SIZE 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C +#define GL_HISTOGRAM_SINK 0x802D +#define GL_MINMAX 0x802E +#define GL_MINMAX_FORMAT 0x802F +#define GL_MINMAX_SINK 0x8030 +#define GL_TABLE_TOO_LARGE 0x8031 +#define GL_COLOR_MATRIX 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB +#define GL_COLOR_TABLE 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2 +#define GL_PROXY_COLOR_TABLE 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5 +#define GL_COLOR_TABLE_SCALE 0x80D6 +#define GL_COLOR_TABLE_BIAS 0x80D7 +#define GL_COLOR_TABLE_FORMAT 0x80D8 +#define GL_COLOR_TABLE_WIDTH 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF +#define GL_CONSTANT_BORDER 0x8151 +#define GL_REPLICATE_BORDER 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR 0x8154 +#endif + +#ifndef GL_VERSION_1_3 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX 0x84E6 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_MULTISAMPLE_BIT 0x20000000 +#define GL_NORMAL_MAP 0x8511 +#define GL_REFLECTION_MAP 0x8512 +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_COMPRESSED_ALPHA 0x84E9 +#define GL_COMPRESSED_LUMINANCE 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB +#define GL_COMPRESSED_INTENSITY 0x84EC +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CLAMP_TO_BORDER 0x812D +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_SOURCE0_RGB 0x8580 +#define GL_SOURCE1_RGB 0x8581 +#define GL_SOURCE2_RGB 0x8582 +#define GL_SOURCE0_ALPHA 0x8588 +#define GL_SOURCE1_ALPHA 0x8589 +#define GL_SOURCE2_ALPHA 0x858A +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_RGB 0x8592 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND2_ALPHA 0x859A +#define GL_RGB_SCALE 0x8573 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_SUBTRACT 0x84E7 +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF +#endif + +#ifndef GL_VERSION_1_4 +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_POINT_SIZE_MIN 0x8126 +#define GL_POINT_SIZE_MAX 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_POINT_DISTANCE_ATTENUATION 0x8129 +#define GL_GENERATE_MIPMAP 0x8191 +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_FOG_COORDINATE_SOURCE 0x8450 +#define GL_FOG_COORDINATE 0x8451 +#define GL_FRAGMENT_DEPTH 0x8452 +#define GL_CURRENT_FOG_COORDINATE 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456 +#define GL_FOG_COORDINATE_ARRAY 0x8457 +#define GL_COLOR_SUM 0x8458 +#define GL_CURRENT_SECONDARY_COLOR 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D +#define GL_SECONDARY_COLOR_ARRAY 0x845E +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_TEXTURE_FILTER_CONTROL 0x8500 +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_DEPTH_TEXTURE_MODE 0x884B +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_COMPARE_R_TO_TEXTURE 0x884E +#endif + +#ifndef GL_VERSION_1_5 +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_CURRENT_QUERY 0x8865 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_SAMPLES_PASSED 0x8914 +#define GL_FOG_COORD_SRC GL_FOG_COORDINATE_SOURCE +#define GL_FOG_COORD GL_FOG_COORDINATE +#define GL_CURRENT_FOG_COORD GL_CURRENT_FOG_COORDINATE +#define GL_FOG_COORD_ARRAY_TYPE GL_FOG_COORDINATE_ARRAY_TYPE +#define GL_FOG_COORD_ARRAY_STRIDE GL_FOG_COORDINATE_ARRAY_STRIDE +#define GL_FOG_COORD_ARRAY_POINTER GL_FOG_COORDINATE_ARRAY_POINTER +#define GL_FOG_COORD_ARRAY GL_FOG_COORDINATE_ARRAY +#define GL_FOG_COORD_ARRAY_BUFFER_BINDING GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING +#define GL_SRC0_RGB GL_SOURCE0_RGB +#define GL_SRC1_RGB GL_SOURCE1_RGB +#define GL_SRC2_RGB GL_SOURCE2_RGB +#define GL_SRC0_ALPHA GL_SOURCE0_ALPHA +#define GL_SRC1_ALPHA GL_SOURCE1_ALPHA +#define GL_SRC2_ALPHA GL_SOURCE2_ALPHA +#endif + +#ifndef GL_VERSION_2_0 +#define GL_BLEND_EQUATION_RGB GL_BLEND_EQUATION +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_POINT_SPRITE 0x8861 +#define GL_COORD_REPLACE 0x8862 +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_MAX_TEXTURE_COORDS 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_SHADER_TYPE 0x8B4F +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_DELETE_STATUS 0x8B80 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#endif + +#ifndef GL_ARB_multitexture +#define GL_TEXTURE0_ARB 0x84C0 +#define GL_TEXTURE1_ARB 0x84C1 +#define GL_TEXTURE2_ARB 0x84C2 +#define GL_TEXTURE3_ARB 0x84C3 +#define GL_TEXTURE4_ARB 0x84C4 +#define GL_TEXTURE5_ARB 0x84C5 +#define GL_TEXTURE6_ARB 0x84C6 +#define GL_TEXTURE7_ARB 0x84C7 +#define GL_TEXTURE8_ARB 0x84C8 +#define GL_TEXTURE9_ARB 0x84C9 +#define GL_TEXTURE10_ARB 0x84CA +#define GL_TEXTURE11_ARB 0x84CB +#define GL_TEXTURE12_ARB 0x84CC +#define GL_TEXTURE13_ARB 0x84CD +#define GL_TEXTURE14_ARB 0x84CE +#define GL_TEXTURE15_ARB 0x84CF +#define GL_TEXTURE16_ARB 0x84D0 +#define GL_TEXTURE17_ARB 0x84D1 +#define GL_TEXTURE18_ARB 0x84D2 +#define GL_TEXTURE19_ARB 0x84D3 +#define GL_TEXTURE20_ARB 0x84D4 +#define GL_TEXTURE21_ARB 0x84D5 +#define GL_TEXTURE22_ARB 0x84D6 +#define GL_TEXTURE23_ARB 0x84D7 +#define GL_TEXTURE24_ARB 0x84D8 +#define GL_TEXTURE25_ARB 0x84D9 +#define GL_TEXTURE26_ARB 0x84DA +#define GL_TEXTURE27_ARB 0x84DB +#define GL_TEXTURE28_ARB 0x84DC +#define GL_TEXTURE29_ARB 0x84DD +#define GL_TEXTURE30_ARB 0x84DE +#define GL_TEXTURE31_ARB 0x84DF +#define GL_ACTIVE_TEXTURE_ARB 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 +#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 +#endif + +#ifndef GL_ARB_transpose_matrix +#define GL_TRANSPOSE_MODELVIEW_MATRIX_ARB 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX_ARB 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX_ARB 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX_ARB 0x84E6 +#endif + +#ifndef GL_ARB_multisample +#define GL_MULTISAMPLE_ARB 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F +#define GL_SAMPLE_COVERAGE_ARB 0x80A0 +#define GL_SAMPLE_BUFFERS_ARB 0x80A8 +#define GL_SAMPLES_ARB 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB +#define GL_MULTISAMPLE_BIT_ARB 0x20000000 +#endif + +#ifndef GL_ARB_texture_env_add +#endif + +#ifndef GL_ARB_texture_cube_map +#define GL_NORMAL_MAP_ARB 0x8511 +#define GL_REFLECTION_MAP_ARB 0x8512 +#define GL_TEXTURE_CUBE_MAP_ARB 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C +#endif + +#ifndef GL_ARB_texture_compression +#define GL_COMPRESSED_ALPHA_ARB 0x84E9 +#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB +#define GL_COMPRESSED_INTENSITY_ARB 0x84EC +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0 +#define GL_TEXTURE_COMPRESSED_ARB 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 +#endif + +#ifndef GL_ARB_texture_border_clamp +#define GL_CLAMP_TO_BORDER_ARB 0x812D +#endif + +#ifndef GL_ARB_point_parameters +#define GL_POINT_SIZE_MIN_ARB 0x8126 +#define GL_POINT_SIZE_MAX_ARB 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_ARB 0x8128 +#define GL_POINT_DISTANCE_ATTENUATION_ARB 0x8129 +#endif + +#ifndef GL_ARB_vertex_blend +#define GL_MAX_VERTEX_UNITS_ARB 0x86A4 +#define GL_ACTIVE_VERTEX_UNITS_ARB 0x86A5 +#define GL_WEIGHT_SUM_UNITY_ARB 0x86A6 +#define GL_VERTEX_BLEND_ARB 0x86A7 +#define GL_CURRENT_WEIGHT_ARB 0x86A8 +#define GL_WEIGHT_ARRAY_TYPE_ARB 0x86A9 +#define GL_WEIGHT_ARRAY_STRIDE_ARB 0x86AA +#define GL_WEIGHT_ARRAY_SIZE_ARB 0x86AB +#define GL_WEIGHT_ARRAY_POINTER_ARB 0x86AC +#define GL_WEIGHT_ARRAY_ARB 0x86AD +#define GL_MODELVIEW0_ARB 0x1700 +#define GL_MODELVIEW1_ARB 0x850A +#define GL_MODELVIEW2_ARB 0x8722 +#define GL_MODELVIEW3_ARB 0x8723 +#define GL_MODELVIEW4_ARB 0x8724 +#define GL_MODELVIEW5_ARB 0x8725 +#define GL_MODELVIEW6_ARB 0x8726 +#define GL_MODELVIEW7_ARB 0x8727 +#define GL_MODELVIEW8_ARB 0x8728 +#define GL_MODELVIEW9_ARB 0x8729 +#define GL_MODELVIEW10_ARB 0x872A +#define GL_MODELVIEW11_ARB 0x872B +#define GL_MODELVIEW12_ARB 0x872C +#define GL_MODELVIEW13_ARB 0x872D +#define GL_MODELVIEW14_ARB 0x872E +#define GL_MODELVIEW15_ARB 0x872F +#define GL_MODELVIEW16_ARB 0x8730 +#define GL_MODELVIEW17_ARB 0x8731 +#define GL_MODELVIEW18_ARB 0x8732 +#define GL_MODELVIEW19_ARB 0x8733 +#define GL_MODELVIEW20_ARB 0x8734 +#define GL_MODELVIEW21_ARB 0x8735 +#define GL_MODELVIEW22_ARB 0x8736 +#define GL_MODELVIEW23_ARB 0x8737 +#define GL_MODELVIEW24_ARB 0x8738 +#define GL_MODELVIEW25_ARB 0x8739 +#define GL_MODELVIEW26_ARB 0x873A +#define GL_MODELVIEW27_ARB 0x873B +#define GL_MODELVIEW28_ARB 0x873C +#define GL_MODELVIEW29_ARB 0x873D +#define GL_MODELVIEW30_ARB 0x873E +#define GL_MODELVIEW31_ARB 0x873F +#endif + +#ifndef GL_ARB_matrix_palette +#define GL_MATRIX_PALETTE_ARB 0x8840 +#define GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB 0x8841 +#define GL_MAX_PALETTE_MATRICES_ARB 0x8842 +#define GL_CURRENT_PALETTE_MATRIX_ARB 0x8843 +#define GL_MATRIX_INDEX_ARRAY_ARB 0x8844 +#define GL_CURRENT_MATRIX_INDEX_ARB 0x8845 +#define GL_MATRIX_INDEX_ARRAY_SIZE_ARB 0x8846 +#define GL_MATRIX_INDEX_ARRAY_TYPE_ARB 0x8847 +#define GL_MATRIX_INDEX_ARRAY_STRIDE_ARB 0x8848 +#define GL_MATRIX_INDEX_ARRAY_POINTER_ARB 0x8849 +#endif + +#ifndef GL_ARB_texture_env_combine +#define GL_COMBINE_ARB 0x8570 +#define GL_COMBINE_RGB_ARB 0x8571 +#define GL_COMBINE_ALPHA_ARB 0x8572 +#define GL_SOURCE0_RGB_ARB 0x8580 +#define GL_SOURCE1_RGB_ARB 0x8581 +#define GL_SOURCE2_RGB_ARB 0x8582 +#define GL_SOURCE0_ALPHA_ARB 0x8588 +#define GL_SOURCE1_ALPHA_ARB 0x8589 +#define GL_SOURCE2_ALPHA_ARB 0x858A +#define GL_OPERAND0_RGB_ARB 0x8590 +#define GL_OPERAND1_RGB_ARB 0x8591 +#define GL_OPERAND2_RGB_ARB 0x8592 +#define GL_OPERAND0_ALPHA_ARB 0x8598 +#define GL_OPERAND1_ALPHA_ARB 0x8599 +#define GL_OPERAND2_ALPHA_ARB 0x859A +#define GL_RGB_SCALE_ARB 0x8573 +#define GL_ADD_SIGNED_ARB 0x8574 +#define GL_INTERPOLATE_ARB 0x8575 +#define GL_SUBTRACT_ARB 0x84E7 +#define GL_CONSTANT_ARB 0x8576 +#define GL_PRIMARY_COLOR_ARB 0x8577 +#define GL_PREVIOUS_ARB 0x8578 +#endif + +#ifndef GL_ARB_texture_env_crossbar +#endif + +#ifndef GL_ARB_texture_env_dot3 +#define GL_DOT3_RGB_ARB 0x86AE +#define GL_DOT3_RGBA_ARB 0x86AF +#endif + +#ifndef GL_ARB_texture_mirrored_repeat +#define GL_MIRRORED_REPEAT_ARB 0x8370 +#endif + +#ifndef GL_ARB_depth_texture +#define GL_DEPTH_COMPONENT16_ARB 0x81A5 +#define GL_DEPTH_COMPONENT24_ARB 0x81A6 +#define GL_DEPTH_COMPONENT32_ARB 0x81A7 +#define GL_TEXTURE_DEPTH_SIZE_ARB 0x884A +#define GL_DEPTH_TEXTURE_MODE_ARB 0x884B +#endif + +#ifndef GL_ARB_shadow +#define GL_TEXTURE_COMPARE_MODE_ARB 0x884C +#define GL_TEXTURE_COMPARE_FUNC_ARB 0x884D +#define GL_COMPARE_R_TO_TEXTURE_ARB 0x884E +#endif + +#ifndef GL_ARB_shadow_ambient +#define GL_TEXTURE_COMPARE_FAIL_VALUE_ARB 0x80BF +#endif + +#ifndef GL_ARB_window_pos +#endif + +#ifndef GL_ARB_vertex_program +#define GL_COLOR_SUM_ARB 0x8458 +#define GL_VERTEX_PROGRAM_ARB 0x8620 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626 +#define GL_PROGRAM_LENGTH_ARB 0x8627 +#define GL_PROGRAM_STRING_ARB 0x8628 +#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E +#define GL_MAX_PROGRAM_MATRICES_ARB 0x862F +#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640 +#define GL_CURRENT_MATRIX_ARB 0x8641 +#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645 +#define GL_PROGRAM_ERROR_POSITION_ARB 0x864B +#define GL_PROGRAM_BINDING_ARB 0x8677 +#define GL_MAX_VERTEX_ATTRIBS_ARB 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A +#define GL_PROGRAM_ERROR_STRING_ARB 0x8874 +#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 +#define GL_PROGRAM_FORMAT_ARB 0x8876 +#define GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0 +#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1 +#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2 +#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3 +#define GL_PROGRAM_TEMPORARIES_ARB 0x88A4 +#define GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5 +#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6 +#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7 +#define GL_PROGRAM_PARAMETERS_ARB 0x88A8 +#define GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9 +#define GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA +#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB +#define GL_PROGRAM_ATTRIBS_ARB 0x88AC +#define GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD +#define GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE +#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF +#define GL_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B0 +#define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1 +#define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2 +#define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3 +#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4 +#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5 +#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6 +#define GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7 +#define GL_MATRIX0_ARB 0x88C0 +#define GL_MATRIX1_ARB 0x88C1 +#define GL_MATRIX2_ARB 0x88C2 +#define GL_MATRIX3_ARB 0x88C3 +#define GL_MATRIX4_ARB 0x88C4 +#define GL_MATRIX5_ARB 0x88C5 +#define GL_MATRIX6_ARB 0x88C6 +#define GL_MATRIX7_ARB 0x88C7 +#define GL_MATRIX8_ARB 0x88C8 +#define GL_MATRIX9_ARB 0x88C9 +#define GL_MATRIX10_ARB 0x88CA +#define GL_MATRIX11_ARB 0x88CB +#define GL_MATRIX12_ARB 0x88CC +#define GL_MATRIX13_ARB 0x88CD +#define GL_MATRIX14_ARB 0x88CE +#define GL_MATRIX15_ARB 0x88CF +#define GL_MATRIX16_ARB 0x88D0 +#define GL_MATRIX17_ARB 0x88D1 +#define GL_MATRIX18_ARB 0x88D2 +#define GL_MATRIX19_ARB 0x88D3 +#define GL_MATRIX20_ARB 0x88D4 +#define GL_MATRIX21_ARB 0x88D5 +#define GL_MATRIX22_ARB 0x88D6 +#define GL_MATRIX23_ARB 0x88D7 +#define GL_MATRIX24_ARB 0x88D8 +#define GL_MATRIX25_ARB 0x88D9 +#define GL_MATRIX26_ARB 0x88DA +#define GL_MATRIX27_ARB 0x88DB +#define GL_MATRIX28_ARB 0x88DC +#define GL_MATRIX29_ARB 0x88DD +#define GL_MATRIX30_ARB 0x88DE +#define GL_MATRIX31_ARB 0x88DF +#endif + +#ifndef GL_ARB_fragment_program +#define GL_FRAGMENT_PROGRAM_ARB 0x8804 +#define GL_PROGRAM_ALU_INSTRUCTIONS_ARB 0x8805 +#define GL_PROGRAM_TEX_INSTRUCTIONS_ARB 0x8806 +#define GL_PROGRAM_TEX_INDIRECTIONS_ARB 0x8807 +#define GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808 +#define GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809 +#define GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A +#define GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B +#define GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C +#define GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D +#define GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E +#define GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F +#define GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810 +#define GL_MAX_TEXTURE_COORDS_ARB 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872 +#endif + +#ifndef GL_ARB_vertex_buffer_object +#define GL_BUFFER_SIZE_ARB 0x8764 +#define GL_BUFFER_USAGE_ARB 0x8765 +#define GL_ARRAY_BUFFER_ARB 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 +#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F +#define GL_READ_ONLY_ARB 0x88B8 +#define GL_WRITE_ONLY_ARB 0x88B9 +#define GL_READ_WRITE_ARB 0x88BA +#define GL_BUFFER_ACCESS_ARB 0x88BB +#define GL_BUFFER_MAPPED_ARB 0x88BC +#define GL_BUFFER_MAP_POINTER_ARB 0x88BD +#define GL_STREAM_DRAW_ARB 0x88E0 +#define GL_STREAM_READ_ARB 0x88E1 +#define GL_STREAM_COPY_ARB 0x88E2 +#define GL_STATIC_DRAW_ARB 0x88E4 +#define GL_STATIC_READ_ARB 0x88E5 +#define GL_STATIC_COPY_ARB 0x88E6 +#define GL_DYNAMIC_DRAW_ARB 0x88E8 +#define GL_DYNAMIC_READ_ARB 0x88E9 +#define GL_DYNAMIC_COPY_ARB 0x88EA +#endif + +#ifndef GL_ARB_occlusion_query +#define GL_QUERY_COUNTER_BITS_ARB 0x8864 +#define GL_CURRENT_QUERY_ARB 0x8865 +#define GL_QUERY_RESULT_ARB 0x8866 +#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 +#define GL_SAMPLES_PASSED_ARB 0x8914 +#endif + +#ifndef GL_ARB_shader_objects +#define GL_PROGRAM_OBJECT_ARB 0x8B40 +#define GL_SHADER_OBJECT_ARB 0x8B48 +#define GL_OBJECT_TYPE_ARB 0x8B4E +#define GL_OBJECT_SUBTYPE_ARB 0x8B4F +#define GL_FLOAT_VEC2_ARB 0x8B50 +#define GL_FLOAT_VEC3_ARB 0x8B51 +#define GL_FLOAT_VEC4_ARB 0x8B52 +#define GL_INT_VEC2_ARB 0x8B53 +#define GL_INT_VEC3_ARB 0x8B54 +#define GL_INT_VEC4_ARB 0x8B55 +#define GL_BOOL_ARB 0x8B56 +#define GL_BOOL_VEC2_ARB 0x8B57 +#define GL_BOOL_VEC3_ARB 0x8B58 +#define GL_BOOL_VEC4_ARB 0x8B59 +#define GL_FLOAT_MAT2_ARB 0x8B5A +#define GL_FLOAT_MAT3_ARB 0x8B5B +#define GL_FLOAT_MAT4_ARB 0x8B5C +#define GL_SAMPLER_1D_ARB 0x8B5D +#define GL_SAMPLER_2D_ARB 0x8B5E +#define GL_SAMPLER_3D_ARB 0x8B5F +#define GL_SAMPLER_CUBE_ARB 0x8B60 +#define GL_SAMPLER_1D_SHADOW_ARB 0x8B61 +#define GL_SAMPLER_2D_SHADOW_ARB 0x8B62 +#define GL_SAMPLER_2D_RECT_ARB 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64 +#define GL_OBJECT_DELETE_STATUS_ARB 0x8B80 +#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 +#define GL_OBJECT_LINK_STATUS_ARB 0x8B82 +#define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83 +#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 +#define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85 +#define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86 +#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87 +#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88 +#endif + +#ifndef GL_ARB_vertex_shader +#define GL_VERTEX_SHADER_ARB 0x8B31 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 0x8B4A +#define GL_MAX_VARYING_FLOATS_ARB 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB 0x8B4D +#define GL_OBJECT_ACTIVE_ATTRIBUTES_ARB 0x8B89 +#define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A +#endif + +#ifndef GL_ARB_fragment_shader +#define GL_FRAGMENT_SHADER_ARB 0x8B30 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB 0x8B49 +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B +#endif + +#ifndef GL_ARB_shading_language_100 +#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C +#endif + +#ifndef GL_ARB_texture_non_power_of_two +#endif + +#ifndef GL_ARB_point_sprite +#define GL_POINT_SPRITE_ARB 0x8861 +#define GL_COORD_REPLACE_ARB 0x8862 +#endif + +#ifndef GL_ARB_fragment_program_shadow +#endif + +#ifndef GL_ARB_draw_buffers +#define GL_MAX_DRAW_BUFFERS_ARB 0x8824 +#define GL_DRAW_BUFFER0_ARB 0x8825 +#define GL_DRAW_BUFFER1_ARB 0x8826 +#define GL_DRAW_BUFFER2_ARB 0x8827 +#define GL_DRAW_BUFFER3_ARB 0x8828 +#define GL_DRAW_BUFFER4_ARB 0x8829 +#define GL_DRAW_BUFFER5_ARB 0x882A +#define GL_DRAW_BUFFER6_ARB 0x882B +#define GL_DRAW_BUFFER7_ARB 0x882C +#define GL_DRAW_BUFFER8_ARB 0x882D +#define GL_DRAW_BUFFER9_ARB 0x882E +#define GL_DRAW_BUFFER10_ARB 0x882F +#define GL_DRAW_BUFFER11_ARB 0x8830 +#define GL_DRAW_BUFFER12_ARB 0x8831 +#define GL_DRAW_BUFFER13_ARB 0x8832 +#define GL_DRAW_BUFFER14_ARB 0x8833 +#define GL_DRAW_BUFFER15_ARB 0x8834 +#endif + +#ifndef GL_ARB_texture_rectangle +#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_ARB 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 +#endif + +#ifndef GL_ARB_color_buffer_float +#define GL_RGBA_FLOAT_MODE_ARB 0x8820 +#define GL_CLAMP_VERTEX_COLOR_ARB 0x891A +#define GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B +#define GL_CLAMP_READ_COLOR_ARB 0x891C +#define GL_FIXED_ONLY_ARB 0x891D +#endif + +#ifndef GL_ARB_half_float_pixel +#define GL_HALF_FLOAT_ARB 0x140B +#endif + +#ifndef GL_ARB_texture_float +#define GL_TEXTURE_RED_TYPE_ARB 0x8C10 +#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11 +#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13 +#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15 +#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16 +#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17 +#define GL_RGBA32F_ARB 0x8814 +#define GL_RGB32F_ARB 0x8815 +#define GL_ALPHA32F_ARB 0x8816 +#define GL_INTENSITY32F_ARB 0x8817 +#define GL_LUMINANCE32F_ARB 0x8818 +#define GL_LUMINANCE_ALPHA32F_ARB 0x8819 +#define GL_RGBA16F_ARB 0x881A +#define GL_RGB16F_ARB 0x881B +#define GL_ALPHA16F_ARB 0x881C +#define GL_INTENSITY16F_ARB 0x881D +#define GL_LUMINANCE16F_ARB 0x881E +#define GL_LUMINANCE_ALPHA16F_ARB 0x881F +#endif + +#ifndef GL_ARB_pixel_buffer_object +#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF +#endif + +#ifndef GL_EXT_abgr +#define GL_ABGR_EXT 0x8000 +#endif + +#ifndef GL_EXT_blend_color +#define GL_CONSTANT_COLOR_EXT 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR_EXT 0x8002 +#define GL_CONSTANT_ALPHA_EXT 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA_EXT 0x8004 +#define GL_BLEND_COLOR_EXT 0x8005 +#endif + +#ifndef GL_EXT_polygon_offset +#define GL_POLYGON_OFFSET_EXT 0x8037 +#define GL_POLYGON_OFFSET_FACTOR_EXT 0x8038 +#define GL_POLYGON_OFFSET_BIAS_EXT 0x8039 +#endif + +#ifndef GL_EXT_texture +#define GL_ALPHA4_EXT 0x803B +#define GL_ALPHA8_EXT 0x803C +#define GL_ALPHA12_EXT 0x803D +#define GL_ALPHA16_EXT 0x803E +#define GL_LUMINANCE4_EXT 0x803F +#define GL_LUMINANCE8_EXT 0x8040 +#define GL_LUMINANCE12_EXT 0x8041 +#define GL_LUMINANCE16_EXT 0x8042 +#define GL_LUMINANCE4_ALPHA4_EXT 0x8043 +#define GL_LUMINANCE6_ALPHA2_EXT 0x8044 +#define GL_LUMINANCE8_ALPHA8_EXT 0x8045 +#define GL_LUMINANCE12_ALPHA4_EXT 0x8046 +#define GL_LUMINANCE12_ALPHA12_EXT 0x8047 +#define GL_LUMINANCE16_ALPHA16_EXT 0x8048 +#define GL_INTENSITY_EXT 0x8049 +#define GL_INTENSITY4_EXT 0x804A +#define GL_INTENSITY8_EXT 0x804B +#define GL_INTENSITY12_EXT 0x804C +#define GL_INTENSITY16_EXT 0x804D +#define GL_RGB2_EXT 0x804E +#define GL_RGB4_EXT 0x804F +#define GL_RGB5_EXT 0x8050 +#define GL_RGB8_EXT 0x8051 +#define GL_RGB10_EXT 0x8052 +#define GL_RGB12_EXT 0x8053 +#define GL_RGB16_EXT 0x8054 +#define GL_RGBA2_EXT 0x8055 +#define GL_RGBA4_EXT 0x8056 +#define GL_RGB5_A1_EXT 0x8057 +#define GL_RGBA8_EXT 0x8058 +#define GL_RGB10_A2_EXT 0x8059 +#define GL_RGBA12_EXT 0x805A +#define GL_RGBA16_EXT 0x805B +#define GL_TEXTURE_RED_SIZE_EXT 0x805C +#define GL_TEXTURE_GREEN_SIZE_EXT 0x805D +#define GL_TEXTURE_BLUE_SIZE_EXT 0x805E +#define GL_TEXTURE_ALPHA_SIZE_EXT 0x805F +#define GL_TEXTURE_LUMINANCE_SIZE_EXT 0x8060 +#define GL_TEXTURE_INTENSITY_SIZE_EXT 0x8061 +#define GL_REPLACE_EXT 0x8062 +#define GL_PROXY_TEXTURE_1D_EXT 0x8063 +#define GL_PROXY_TEXTURE_2D_EXT 0x8064 +#define GL_TEXTURE_TOO_LARGE_EXT 0x8065 +#endif + +#ifndef GL_EXT_texture3D +#define GL_PACK_SKIP_IMAGES_EXT 0x806B +#define GL_PACK_IMAGE_HEIGHT_EXT 0x806C +#define GL_UNPACK_SKIP_IMAGES_EXT 0x806D +#define GL_UNPACK_IMAGE_HEIGHT_EXT 0x806E +#define GL_TEXTURE_3D_EXT 0x806F +#define GL_PROXY_TEXTURE_3D_EXT 0x8070 +#define GL_TEXTURE_DEPTH_EXT 0x8071 +#define GL_TEXTURE_WRAP_R_EXT 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE_EXT 0x8073 +#endif + +#ifndef GL_SGIS_texture_filter4 +#define GL_FILTER4_SGIS 0x8146 +#define GL_TEXTURE_FILTER4_SIZE_SGIS 0x8147 +#endif + +#ifndef GL_EXT_subtexture +#endif + +#ifndef GL_EXT_copy_texture +#endif + +#ifndef GL_EXT_histogram +#define GL_HISTOGRAM_EXT 0x8024 +#define GL_PROXY_HISTOGRAM_EXT 0x8025 +#define GL_HISTOGRAM_WIDTH_EXT 0x8026 +#define GL_HISTOGRAM_FORMAT_EXT 0x8027 +#define GL_HISTOGRAM_RED_SIZE_EXT 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE_EXT 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE_EXT 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE_EXT 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE_EXT 0x802C +#define GL_HISTOGRAM_SINK_EXT 0x802D +#define GL_MINMAX_EXT 0x802E +#define GL_MINMAX_FORMAT_EXT 0x802F +#define GL_MINMAX_SINK_EXT 0x8030 +#define GL_TABLE_TOO_LARGE_EXT 0x8031 +#endif + +#ifndef GL_EXT_convolution +#define GL_CONVOLUTION_1D_EXT 0x8010 +#define GL_CONVOLUTION_2D_EXT 0x8011 +#define GL_SEPARABLE_2D_EXT 0x8012 +#define GL_CONVOLUTION_BORDER_MODE_EXT 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE_EXT 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS_EXT 0x8015 +#define GL_REDUCE_EXT 0x8016 +#define GL_CONVOLUTION_FORMAT_EXT 0x8017 +#define GL_CONVOLUTION_WIDTH_EXT 0x8018 +#define GL_CONVOLUTION_HEIGHT_EXT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH_EXT 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT_EXT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE_EXT 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE_EXT 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE_EXT 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE_EXT 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS_EXT 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS_EXT 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS_EXT 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS_EXT 0x8023 +#endif + +#ifndef GL_SGI_color_matrix +#define GL_COLOR_MATRIX_SGI 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE_SGI 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS_SGI 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI 0x80BB +#endif + +#ifndef GL_SGI_color_table +#define GL_COLOR_TABLE_SGI 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D2 +#define GL_PROXY_COLOR_TABLE_SGI 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D5 +#define GL_COLOR_TABLE_SCALE_SGI 0x80D6 +#define GL_COLOR_TABLE_BIAS_SGI 0x80D7 +#define GL_COLOR_TABLE_FORMAT_SGI 0x80D8 +#define GL_COLOR_TABLE_WIDTH_SGI 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE_SGI 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE_SGI 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE_SGI 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE_SGI 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE_SGI 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE_SGI 0x80DF +#endif + +#ifndef GL_SGIS_pixel_texture +#define GL_PIXEL_TEXTURE_SGIS 0x8353 +#define GL_PIXEL_FRAGMENT_RGB_SOURCE_SGIS 0x8354 +#define GL_PIXEL_FRAGMENT_ALPHA_SOURCE_SGIS 0x8355 +#define GL_PIXEL_GROUP_COLOR_SGIS 0x8356 +#endif + +#ifndef GL_SGIX_pixel_texture +#define GL_PIXEL_TEX_GEN_SGIX 0x8139 +#define GL_PIXEL_TEX_GEN_MODE_SGIX 0x832B +#endif + +#ifndef GL_SGIS_texture4D +#define GL_PACK_SKIP_VOLUMES_SGIS 0x8130 +#define GL_PACK_IMAGE_DEPTH_SGIS 0x8131 +#define GL_UNPACK_SKIP_VOLUMES_SGIS 0x8132 +#define GL_UNPACK_IMAGE_DEPTH_SGIS 0x8133 +#define GL_TEXTURE_4D_SGIS 0x8134 +#define GL_PROXY_TEXTURE_4D_SGIS 0x8135 +#define GL_TEXTURE_4DSIZE_SGIS 0x8136 +#define GL_TEXTURE_WRAP_Q_SGIS 0x8137 +#define GL_MAX_4D_TEXTURE_SIZE_SGIS 0x8138 +#define GL_TEXTURE_4D_BINDING_SGIS 0x814F +#endif + +#ifndef GL_SGI_texture_color_table +#define GL_TEXTURE_COLOR_TABLE_SGI 0x80BC +#define GL_PROXY_TEXTURE_COLOR_TABLE_SGI 0x80BD +#endif + +#ifndef GL_EXT_cmyka +#define GL_CMYK_EXT 0x800C +#define GL_CMYKA_EXT 0x800D +#define GL_PACK_CMYK_HINT_EXT 0x800E +#define GL_UNPACK_CMYK_HINT_EXT 0x800F +#endif + +#ifndef GL_EXT_texture_object +#define GL_TEXTURE_PRIORITY_EXT 0x8066 +#define GL_TEXTURE_RESIDENT_EXT 0x8067 +#define GL_TEXTURE_1D_BINDING_EXT 0x8068 +#define GL_TEXTURE_2D_BINDING_EXT 0x8069 +#define GL_TEXTURE_3D_BINDING_EXT 0x806A +#endif + +#ifndef GL_SGIS_detail_texture +#define GL_DETAIL_TEXTURE_2D_SGIS 0x8095 +#define GL_DETAIL_TEXTURE_2D_BINDING_SGIS 0x8096 +#define GL_LINEAR_DETAIL_SGIS 0x8097 +#define GL_LINEAR_DETAIL_ALPHA_SGIS 0x8098 +#define GL_LINEAR_DETAIL_COLOR_SGIS 0x8099 +#define GL_DETAIL_TEXTURE_LEVEL_SGIS 0x809A +#define GL_DETAIL_TEXTURE_MODE_SGIS 0x809B +#define GL_DETAIL_TEXTURE_FUNC_POINTS_SGIS 0x809C +#endif + +#ifndef GL_SGIS_sharpen_texture +#define GL_LINEAR_SHARPEN_SGIS 0x80AD +#define GL_LINEAR_SHARPEN_ALPHA_SGIS 0x80AE +#define GL_LINEAR_SHARPEN_COLOR_SGIS 0x80AF +#define GL_SHARPEN_TEXTURE_FUNC_POINTS_SGIS 0x80B0 +#endif + +#ifndef GL_EXT_packed_pixels +#define GL_UNSIGNED_BYTE_3_3_2_EXT 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4_EXT 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1_EXT 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8_EXT 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2_EXT 0x8036 +#endif + +#ifndef GL_SGIS_texture_lod +#define GL_TEXTURE_MIN_LOD_SGIS 0x813A +#define GL_TEXTURE_MAX_LOD_SGIS 0x813B +#define GL_TEXTURE_BASE_LEVEL_SGIS 0x813C +#define GL_TEXTURE_MAX_LEVEL_SGIS 0x813D +#endif + +#ifndef GL_SGIS_multisample +#define GL_MULTISAMPLE_SGIS 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_SGIS 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_SGIS 0x809F +#define GL_SAMPLE_MASK_SGIS 0x80A0 +#define GL_1PASS_SGIS 0x80A1 +#define GL_2PASS_0_SGIS 0x80A2 +#define GL_2PASS_1_SGIS 0x80A3 +#define GL_4PASS_0_SGIS 0x80A4 +#define GL_4PASS_1_SGIS 0x80A5 +#define GL_4PASS_2_SGIS 0x80A6 +#define GL_4PASS_3_SGIS 0x80A7 +#define GL_SAMPLE_BUFFERS_SGIS 0x80A8 +#define GL_SAMPLES_SGIS 0x80A9 +#define GL_SAMPLE_MASK_VALUE_SGIS 0x80AA +#define GL_SAMPLE_MASK_INVERT_SGIS 0x80AB +#define GL_SAMPLE_PATTERN_SGIS 0x80AC +#endif + +#ifndef GL_EXT_rescale_normal +#define GL_RESCALE_NORMAL_EXT 0x803A +#endif + +#ifndef GL_EXT_vertex_array +#define GL_VERTEX_ARRAY_EXT 0x8074 +#define GL_NORMAL_ARRAY_EXT 0x8075 +#define GL_COLOR_ARRAY_EXT 0x8076 +#define GL_INDEX_ARRAY_EXT 0x8077 +#define GL_TEXTURE_COORD_ARRAY_EXT 0x8078 +#define GL_EDGE_FLAG_ARRAY_EXT 0x8079 +#define GL_VERTEX_ARRAY_SIZE_EXT 0x807A +#define GL_VERTEX_ARRAY_TYPE_EXT 0x807B +#define GL_VERTEX_ARRAY_STRIDE_EXT 0x807C +#define GL_VERTEX_ARRAY_COUNT_EXT 0x807D +#define GL_NORMAL_ARRAY_TYPE_EXT 0x807E +#define GL_NORMAL_ARRAY_STRIDE_EXT 0x807F +#define GL_NORMAL_ARRAY_COUNT_EXT 0x8080 +#define GL_COLOR_ARRAY_SIZE_EXT 0x8081 +#define GL_COLOR_ARRAY_TYPE_EXT 0x8082 +#define GL_COLOR_ARRAY_STRIDE_EXT 0x8083 +#define GL_COLOR_ARRAY_COUNT_EXT 0x8084 +#define GL_INDEX_ARRAY_TYPE_EXT 0x8085 +#define GL_INDEX_ARRAY_STRIDE_EXT 0x8086 +#define GL_INDEX_ARRAY_COUNT_EXT 0x8087 +#define GL_TEXTURE_COORD_ARRAY_SIZE_EXT 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE_EXT 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE_EXT 0x808A +#define GL_TEXTURE_COORD_ARRAY_COUNT_EXT 0x808B +#define GL_EDGE_FLAG_ARRAY_STRIDE_EXT 0x808C +#define GL_EDGE_FLAG_ARRAY_COUNT_EXT 0x808D +#define GL_VERTEX_ARRAY_POINTER_EXT 0x808E +#define GL_NORMAL_ARRAY_POINTER_EXT 0x808F +#define GL_COLOR_ARRAY_POINTER_EXT 0x8090 +#define GL_INDEX_ARRAY_POINTER_EXT 0x8091 +#define GL_TEXTURE_COORD_ARRAY_POINTER_EXT 0x8092 +#define GL_EDGE_FLAG_ARRAY_POINTER_EXT 0x8093 +#endif + +#ifndef GL_EXT_misc_attribute +#endif + +#ifndef GL_SGIS_generate_mipmap +#define GL_GENERATE_MIPMAP_SGIS 0x8191 +#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 +#endif + +#ifndef GL_SGIX_clipmap +#define GL_LINEAR_CLIPMAP_LINEAR_SGIX 0x8170 +#define GL_TEXTURE_CLIPMAP_CENTER_SGIX 0x8171 +#define GL_TEXTURE_CLIPMAP_FRAME_SGIX 0x8172 +#define GL_TEXTURE_CLIPMAP_OFFSET_SGIX 0x8173 +#define GL_TEXTURE_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8174 +#define GL_TEXTURE_CLIPMAP_LOD_OFFSET_SGIX 0x8175 +#define GL_TEXTURE_CLIPMAP_DEPTH_SGIX 0x8176 +#define GL_MAX_CLIPMAP_DEPTH_SGIX 0x8177 +#define GL_MAX_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8178 +#define GL_NEAREST_CLIPMAP_NEAREST_SGIX 0x844D +#define GL_NEAREST_CLIPMAP_LINEAR_SGIX 0x844E +#define GL_LINEAR_CLIPMAP_NEAREST_SGIX 0x844F +#endif + +#ifndef GL_SGIX_shadow +#define GL_TEXTURE_COMPARE_SGIX 0x819A +#define GL_TEXTURE_COMPARE_OPERATOR_SGIX 0x819B +#define GL_TEXTURE_LEQUAL_R_SGIX 0x819C +#define GL_TEXTURE_GEQUAL_R_SGIX 0x819D +#endif + +#ifndef GL_SGIS_texture_edge_clamp +#define GL_CLAMP_TO_EDGE_SGIS 0x812F +#endif + +#ifndef GL_SGIS_texture_border_clamp +#define GL_CLAMP_TO_BORDER_SGIS 0x812D +#endif + +#ifndef GL_EXT_blend_minmax +#define GL_FUNC_ADD_EXT 0x8006 +#define GL_MIN_EXT 0x8007 +#define GL_MAX_EXT 0x8008 +#define GL_BLEND_EQUATION_EXT 0x8009 +#endif + +#ifndef GL_EXT_blend_subtract +#define GL_FUNC_SUBTRACT_EXT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B +#endif + +#ifndef GL_EXT_blend_logic_op +#endif + +#ifndef GL_SGIX_interlace +#define GL_INTERLACE_SGIX 0x8094 +#endif + +#ifndef GL_SGIX_pixel_tiles +#define GL_PIXEL_TILE_BEST_ALIGNMENT_SGIX 0x813E +#define GL_PIXEL_TILE_CACHE_INCREMENT_SGIX 0x813F +#define GL_PIXEL_TILE_WIDTH_SGIX 0x8140 +#define GL_PIXEL_TILE_HEIGHT_SGIX 0x8141 +#define GL_PIXEL_TILE_GRID_WIDTH_SGIX 0x8142 +#define GL_PIXEL_TILE_GRID_HEIGHT_SGIX 0x8143 +#define GL_PIXEL_TILE_GRID_DEPTH_SGIX 0x8144 +#define GL_PIXEL_TILE_CACHE_SIZE_SGIX 0x8145 +#endif + +#ifndef GL_SGIS_texture_select +#define GL_DUAL_ALPHA4_SGIS 0x8110 +#define GL_DUAL_ALPHA8_SGIS 0x8111 +#define GL_DUAL_ALPHA12_SGIS 0x8112 +#define GL_DUAL_ALPHA16_SGIS 0x8113 +#define GL_DUAL_LUMINANCE4_SGIS 0x8114 +#define GL_DUAL_LUMINANCE8_SGIS 0x8115 +#define GL_DUAL_LUMINANCE12_SGIS 0x8116 +#define GL_DUAL_LUMINANCE16_SGIS 0x8117 +#define GL_DUAL_INTENSITY4_SGIS 0x8118 +#define GL_DUAL_INTENSITY8_SGIS 0x8119 +#define GL_DUAL_INTENSITY12_SGIS 0x811A +#define GL_DUAL_INTENSITY16_SGIS 0x811B +#define GL_DUAL_LUMINANCE_ALPHA4_SGIS 0x811C +#define GL_DUAL_LUMINANCE_ALPHA8_SGIS 0x811D +#define GL_QUAD_ALPHA4_SGIS 0x811E +#define GL_QUAD_ALPHA8_SGIS 0x811F +#define GL_QUAD_LUMINANCE4_SGIS 0x8120 +#define GL_QUAD_LUMINANCE8_SGIS 0x8121 +#define GL_QUAD_INTENSITY4_SGIS 0x8122 +#define GL_QUAD_INTENSITY8_SGIS 0x8123 +#define GL_DUAL_TEXTURE_SELECT_SGIS 0x8124 +#define GL_QUAD_TEXTURE_SELECT_SGIS 0x8125 +#endif + +#ifndef GL_SGIX_sprite +#define GL_SPRITE_SGIX 0x8148 +#define GL_SPRITE_MODE_SGIX 0x8149 +#define GL_SPRITE_AXIS_SGIX 0x814A +#define GL_SPRITE_TRANSLATION_SGIX 0x814B +#define GL_SPRITE_AXIAL_SGIX 0x814C +#define GL_SPRITE_OBJECT_ALIGNED_SGIX 0x814D +#define GL_SPRITE_EYE_ALIGNED_SGIX 0x814E +#endif + +#ifndef GL_SGIX_texture_multi_buffer +#define GL_TEXTURE_MULTI_BUFFER_HINT_SGIX 0x812E +#endif + +#ifndef GL_EXT_point_parameters +#define GL_POINT_SIZE_MIN_EXT 0x8126 +#define GL_POINT_SIZE_MAX_EXT 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_EXT 0x8128 +#define GL_DISTANCE_ATTENUATION_EXT 0x8129 +#endif + +#ifndef GL_SGIS_point_parameters +#define GL_POINT_SIZE_MIN_SGIS 0x8126 +#define GL_POINT_SIZE_MAX_SGIS 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_SGIS 0x8128 +#define GL_DISTANCE_ATTENUATION_SGIS 0x8129 +#endif + +#ifndef GL_SGIX_instruments +#define GL_INSTRUMENT_BUFFER_POINTER_SGIX 0x8180 +#define GL_INSTRUMENT_MEASUREMENTS_SGIX 0x8181 +#endif + +#ifndef GL_SGIX_texture_scale_bias +#define GL_POST_TEXTURE_FILTER_BIAS_SGIX 0x8179 +#define GL_POST_TEXTURE_FILTER_SCALE_SGIX 0x817A +#define GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX 0x817B +#define GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX 0x817C +#endif + +#ifndef GL_SGIX_framezoom +#define GL_FRAMEZOOM_SGIX 0x818B +#define GL_FRAMEZOOM_FACTOR_SGIX 0x818C +#define GL_MAX_FRAMEZOOM_FACTOR_SGIX 0x818D +#endif + +#ifndef GL_SGIX_tag_sample_buffer +#endif + +#ifndef GL_FfdMaskSGIX +#define GL_TEXTURE_DEFORMATION_BIT_SGIX 0x00000001 +#define GL_GEOMETRY_DEFORMATION_BIT_SGIX 0x00000002 +#endif + +#ifndef GL_SGIX_polynomial_ffd +#define GL_GEOMETRY_DEFORMATION_SGIX 0x8194 +#define GL_TEXTURE_DEFORMATION_SGIX 0x8195 +#define GL_DEFORMATIONS_MASK_SGIX 0x8196 +#define GL_MAX_DEFORMATION_ORDER_SGIX 0x8197 +#endif + +#ifndef GL_SGIX_reference_plane +#define GL_REFERENCE_PLANE_SGIX 0x817D +#define GL_REFERENCE_PLANE_EQUATION_SGIX 0x817E +#endif + +#ifndef GL_SGIX_flush_raster +#endif + +#ifndef GL_SGIX_depth_texture +#define GL_DEPTH_COMPONENT16_SGIX 0x81A5 +#define GL_DEPTH_COMPONENT24_SGIX 0x81A6 +#define GL_DEPTH_COMPONENT32_SGIX 0x81A7 +#endif + +#ifndef GL_SGIS_fog_function +#define GL_FOG_FUNC_SGIS 0x812A +#define GL_FOG_FUNC_POINTS_SGIS 0x812B +#define GL_MAX_FOG_FUNC_POINTS_SGIS 0x812C +#endif + +#ifndef GL_SGIX_fog_offset +#define GL_FOG_OFFSET_SGIX 0x8198 +#define GL_FOG_OFFSET_VALUE_SGIX 0x8199 +#endif + +#ifndef GL_HP_image_transform +#define GL_IMAGE_SCALE_X_HP 0x8155 +#define GL_IMAGE_SCALE_Y_HP 0x8156 +#define GL_IMAGE_TRANSLATE_X_HP 0x8157 +#define GL_IMAGE_TRANSLATE_Y_HP 0x8158 +#define GL_IMAGE_ROTATE_ANGLE_HP 0x8159 +#define GL_IMAGE_ROTATE_ORIGIN_X_HP 0x815A +#define GL_IMAGE_ROTATE_ORIGIN_Y_HP 0x815B +#define GL_IMAGE_MAG_FILTER_HP 0x815C +#define GL_IMAGE_MIN_FILTER_HP 0x815D +#define GL_IMAGE_CUBIC_WEIGHT_HP 0x815E +#define GL_CUBIC_HP 0x815F +#define GL_AVERAGE_HP 0x8160 +#define GL_IMAGE_TRANSFORM_2D_HP 0x8161 +#define GL_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8162 +#define GL_PROXY_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8163 +#endif + +#ifndef GL_HP_convolution_border_modes +#define GL_IGNORE_BORDER_HP 0x8150 +#define GL_CONSTANT_BORDER_HP 0x8151 +#define GL_REPLICATE_BORDER_HP 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR_HP 0x8154 +#endif + +#ifndef GL_INGR_palette_buffer +#endif + +#ifndef GL_SGIX_texture_add_env +#define GL_TEXTURE_ENV_BIAS_SGIX 0x80BE +#endif + +#ifndef GL_EXT_color_subtable +#endif + +#ifndef GL_PGI_vertex_hints +#define GL_VERTEX_DATA_HINT_PGI 0x1A22A +#define GL_VERTEX_CONSISTENT_HINT_PGI 0x1A22B +#define GL_MATERIAL_SIDE_HINT_PGI 0x1A22C +#define GL_MAX_VERTEX_HINT_PGI 0x1A22D +#define GL_COLOR3_BIT_PGI 0x00010000 +#define GL_COLOR4_BIT_PGI 0x00020000 +#define GL_EDGEFLAG_BIT_PGI 0x00040000 +#define GL_INDEX_BIT_PGI 0x00080000 +#define GL_MAT_AMBIENT_BIT_PGI 0x00100000 +#define GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI 0x00200000 +#define GL_MAT_DIFFUSE_BIT_PGI 0x00400000 +#define GL_MAT_EMISSION_BIT_PGI 0x00800000 +#define GL_MAT_COLOR_INDEXES_BIT_PGI 0x01000000 +#define GL_MAT_SHININESS_BIT_PGI 0x02000000 +#define GL_MAT_SPECULAR_BIT_PGI 0x04000000 +#define GL_NORMAL_BIT_PGI 0x08000000 +#define GL_TEXCOORD1_BIT_PGI 0x10000000 +#define GL_TEXCOORD2_BIT_PGI 0x20000000 +#define GL_TEXCOORD3_BIT_PGI 0x40000000 +#define GL_TEXCOORD4_BIT_PGI 0x80000000 +#define GL_VERTEX23_BIT_PGI 0x00000004 +#define GL_VERTEX4_BIT_PGI 0x00000008 +#endif + +#ifndef GL_PGI_misc_hints +#define GL_PREFER_DOUBLEBUFFER_HINT_PGI 0x1A1F8 +#define GL_CONSERVE_MEMORY_HINT_PGI 0x1A1FD +#define GL_RECLAIM_MEMORY_HINT_PGI 0x1A1FE +#define GL_NATIVE_GRAPHICS_HANDLE_PGI 0x1A202 +#define GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI 0x1A203 +#define GL_NATIVE_GRAPHICS_END_HINT_PGI 0x1A204 +#define GL_ALWAYS_FAST_HINT_PGI 0x1A20C +#define GL_ALWAYS_SOFT_HINT_PGI 0x1A20D +#define GL_ALLOW_DRAW_OBJ_HINT_PGI 0x1A20E +#define GL_ALLOW_DRAW_WIN_HINT_PGI 0x1A20F +#define GL_ALLOW_DRAW_FRG_HINT_PGI 0x1A210 +#define GL_ALLOW_DRAW_MEM_HINT_PGI 0x1A211 +#define GL_STRICT_DEPTHFUNC_HINT_PGI 0x1A216 +#define GL_STRICT_LIGHTING_HINT_PGI 0x1A217 +#define GL_STRICT_SCISSOR_HINT_PGI 0x1A218 +#define GL_FULL_STIPPLE_HINT_PGI 0x1A219 +#define GL_CLIP_NEAR_HINT_PGI 0x1A220 +#define GL_CLIP_FAR_HINT_PGI 0x1A221 +#define GL_WIDE_LINE_HINT_PGI 0x1A222 +#define GL_BACK_NORMALS_HINT_PGI 0x1A223 +#endif + +#ifndef GL_EXT_paletted_texture +#define GL_COLOR_INDEX1_EXT 0x80E2 +#define GL_COLOR_INDEX2_EXT 0x80E3 +#define GL_COLOR_INDEX4_EXT 0x80E4 +#define GL_COLOR_INDEX8_EXT 0x80E5 +#define GL_COLOR_INDEX12_EXT 0x80E6 +#define GL_COLOR_INDEX16_EXT 0x80E7 +#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED +#endif + +#ifndef GL_EXT_clip_volume_hint +#define GL_CLIP_VOLUME_CLIPPING_HINT_EXT 0x80F0 +#endif + +#ifndef GL_SGIX_list_priority +#define GL_LIST_PRIORITY_SGIX 0x8182 +#endif + +#ifndef GL_SGIX_ir_instrument1 +#define GL_IR_INSTRUMENT1_SGIX 0x817F +#endif + +#ifndef GL_SGIX_calligraphic_fragment +#define GL_CALLIGRAPHIC_FRAGMENT_SGIX 0x8183 +#endif + +#ifndef GL_SGIX_texture_lod_bias +#define GL_TEXTURE_LOD_BIAS_S_SGIX 0x818E +#define GL_TEXTURE_LOD_BIAS_T_SGIX 0x818F +#define GL_TEXTURE_LOD_BIAS_R_SGIX 0x8190 +#endif + +#ifndef GL_SGIX_shadow_ambient +#define GL_SHADOW_AMBIENT_SGIX 0x80BF +#endif + +#ifndef GL_EXT_index_texture +#endif + +#ifndef GL_EXT_index_material +#define GL_INDEX_MATERIAL_EXT 0x81B8 +#define GL_INDEX_MATERIAL_PARAMETER_EXT 0x81B9 +#define GL_INDEX_MATERIAL_FACE_EXT 0x81BA +#endif + +#ifndef GL_EXT_index_func +#define GL_INDEX_TEST_EXT 0x81B5 +#define GL_INDEX_TEST_FUNC_EXT 0x81B6 +#define GL_INDEX_TEST_REF_EXT 0x81B7 +#endif + +#ifndef GL_EXT_index_array_formats +#define GL_IUI_V2F_EXT 0x81AD +#define GL_IUI_V3F_EXT 0x81AE +#define GL_IUI_N3F_V2F_EXT 0x81AF +#define GL_IUI_N3F_V3F_EXT 0x81B0 +#define GL_T2F_IUI_V2F_EXT 0x81B1 +#define GL_T2F_IUI_V3F_EXT 0x81B2 +#define GL_T2F_IUI_N3F_V2F_EXT 0x81B3 +#define GL_T2F_IUI_N3F_V3F_EXT 0x81B4 +#endif + +#ifndef GL_EXT_compiled_vertex_array +#define GL_ARRAY_ELEMENT_LOCK_FIRST_EXT 0x81A8 +#define GL_ARRAY_ELEMENT_LOCK_COUNT_EXT 0x81A9 +#endif + +#ifndef GL_EXT_cull_vertex +#define GL_CULL_VERTEX_EXT 0x81AA +#define GL_CULL_VERTEX_EYE_POSITION_EXT 0x81AB +#define GL_CULL_VERTEX_OBJECT_POSITION_EXT 0x81AC +#endif + +#ifndef GL_SGIX_ycrcb +#define GL_YCRCB_422_SGIX 0x81BB +#define GL_YCRCB_444_SGIX 0x81BC +#endif + +#ifndef GL_SGIX_fragment_lighting +#define GL_FRAGMENT_LIGHTING_SGIX 0x8400 +#define GL_FRAGMENT_COLOR_MATERIAL_SGIX 0x8401 +#define GL_FRAGMENT_COLOR_MATERIAL_FACE_SGIX 0x8402 +#define GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_SGIX 0x8403 +#define GL_MAX_FRAGMENT_LIGHTS_SGIX 0x8404 +#define GL_MAX_ACTIVE_LIGHTS_SGIX 0x8405 +#define GL_CURRENT_RASTER_NORMAL_SGIX 0x8406 +#define GL_LIGHT_ENV_MODE_SGIX 0x8407 +#define GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_SGIX 0x8408 +#define GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_SGIX 0x8409 +#define GL_FRAGMENT_LIGHT_MODEL_AMBIENT_SGIX 0x840A +#define GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_SGIX 0x840B +#define GL_FRAGMENT_LIGHT0_SGIX 0x840C +#define GL_FRAGMENT_LIGHT1_SGIX 0x840D +#define GL_FRAGMENT_LIGHT2_SGIX 0x840E +#define GL_FRAGMENT_LIGHT3_SGIX 0x840F +#define GL_FRAGMENT_LIGHT4_SGIX 0x8410 +#define GL_FRAGMENT_LIGHT5_SGIX 0x8411 +#define GL_FRAGMENT_LIGHT6_SGIX 0x8412 +#define GL_FRAGMENT_LIGHT7_SGIX 0x8413 +#endif + +#ifndef GL_IBM_rasterpos_clip +#define GL_RASTER_POSITION_UNCLIPPED_IBM 0x19262 +#endif + +#ifndef GL_HP_texture_lighting +#define GL_TEXTURE_LIGHTING_MODE_HP 0x8167 +#define GL_TEXTURE_POST_SPECULAR_HP 0x8168 +#define GL_TEXTURE_PRE_SPECULAR_HP 0x8169 +#endif + +#ifndef GL_EXT_draw_range_elements +#define GL_MAX_ELEMENTS_VERTICES_EXT 0x80E8 +#define GL_MAX_ELEMENTS_INDICES_EXT 0x80E9 +#endif + +#ifndef GL_WIN_phong_shading +#define GL_PHONG_WIN 0x80EA +#define GL_PHONG_HINT_WIN 0x80EB +#endif + +#ifndef GL_WIN_specular_fog +#define GL_FOG_SPECULAR_TEXTURE_WIN 0x80EC +#endif + +#ifndef GL_EXT_light_texture +#define GL_FRAGMENT_MATERIAL_EXT 0x8349 +#define GL_FRAGMENT_NORMAL_EXT 0x834A +#define GL_FRAGMENT_COLOR_EXT 0x834C +#define GL_ATTENUATION_EXT 0x834D +#define GL_SHADOW_ATTENUATION_EXT 0x834E +#define GL_TEXTURE_APPLICATION_MODE_EXT 0x834F +#define GL_TEXTURE_LIGHT_EXT 0x8350 +#define GL_TEXTURE_MATERIAL_FACE_EXT 0x8351 +#define GL_TEXTURE_MATERIAL_PARAMETER_EXT 0x8352 +/* reuse GL_FRAGMENT_DEPTH_EXT */ +#endif + +#ifndef GL_SGIX_blend_alpha_minmax +#define GL_ALPHA_MIN_SGIX 0x8320 +#define GL_ALPHA_MAX_SGIX 0x8321 +#endif + +#ifndef GL_SGIX_impact_pixel_texture +#define GL_PIXEL_TEX_GEN_Q_CEILING_SGIX 0x8184 +#define GL_PIXEL_TEX_GEN_Q_ROUND_SGIX 0x8185 +#define GL_PIXEL_TEX_GEN_Q_FLOOR_SGIX 0x8186 +#define GL_PIXEL_TEX_GEN_ALPHA_REPLACE_SGIX 0x8187 +#define GL_PIXEL_TEX_GEN_ALPHA_NO_REPLACE_SGIX 0x8188 +#define GL_PIXEL_TEX_GEN_ALPHA_LS_SGIX 0x8189 +#define GL_PIXEL_TEX_GEN_ALPHA_MS_SGIX 0x818A +#endif + +#ifndef GL_EXT_bgra +#define GL_BGR_EXT 0x80E0 +#define GL_BGRA_EXT 0x80E1 +#endif + +#ifndef GL_SGIX_async +#define GL_ASYNC_MARKER_SGIX 0x8329 +#endif + +#ifndef GL_SGIX_async_pixel +#define GL_ASYNC_TEX_IMAGE_SGIX 0x835C +#define GL_ASYNC_DRAW_PIXELS_SGIX 0x835D +#define GL_ASYNC_READ_PIXELS_SGIX 0x835E +#define GL_MAX_ASYNC_TEX_IMAGE_SGIX 0x835F +#define GL_MAX_ASYNC_DRAW_PIXELS_SGIX 0x8360 +#define GL_MAX_ASYNC_READ_PIXELS_SGIX 0x8361 +#endif + +#ifndef GL_SGIX_async_histogram +#define GL_ASYNC_HISTOGRAM_SGIX 0x832C +#define GL_MAX_ASYNC_HISTOGRAM_SGIX 0x832D +#endif + +#ifndef GL_INTEL_texture_scissor +#endif + +#ifndef GL_INTEL_parallel_arrays +#define GL_PARALLEL_ARRAYS_INTEL 0x83F4 +#define GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL 0x83F5 +#define GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL 0x83F6 +#define GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL 0x83F7 +#define GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL 0x83F8 +#endif + +#ifndef GL_HP_occlusion_test +#define GL_OCCLUSION_TEST_HP 0x8165 +#define GL_OCCLUSION_TEST_RESULT_HP 0x8166 +#endif + +#ifndef GL_EXT_pixel_transform +#define GL_PIXEL_TRANSFORM_2D_EXT 0x8330 +#define GL_PIXEL_MAG_FILTER_EXT 0x8331 +#define GL_PIXEL_MIN_FILTER_EXT 0x8332 +#define GL_PIXEL_CUBIC_WEIGHT_EXT 0x8333 +#define GL_CUBIC_EXT 0x8334 +#define GL_AVERAGE_EXT 0x8335 +#define GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8336 +#define GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8337 +#define GL_PIXEL_TRANSFORM_2D_MATRIX_EXT 0x8338 +#endif + +#ifndef GL_EXT_pixel_transform_color_table +#endif + +#ifndef GL_EXT_shared_texture_palette +#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB +#endif + +#ifndef GL_EXT_separate_specular_color +#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8 +#define GL_SINGLE_COLOR_EXT 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA +#endif + +#ifndef GL_EXT_secondary_color +#define GL_COLOR_SUM_EXT 0x8458 +#define GL_CURRENT_SECONDARY_COLOR_EXT 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE_EXT 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE_EXT 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER_EXT 0x845D +#define GL_SECONDARY_COLOR_ARRAY_EXT 0x845E +#endif + +#ifndef GL_EXT_texture_perturb_normal +#define GL_PERTURB_EXT 0x85AE +#define GL_TEXTURE_NORMAL_EXT 0x85AF +#endif + +#ifndef GL_EXT_multi_draw_arrays +#endif + +#ifndef GL_EXT_fog_coord +#define GL_FOG_COORDINATE_SOURCE_EXT 0x8450 +#define GL_FOG_COORDINATE_EXT 0x8451 +#define GL_FRAGMENT_DEPTH_EXT 0x8452 +#define GL_CURRENT_FOG_COORDINATE_EXT 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456 +#define GL_FOG_COORDINATE_ARRAY_EXT 0x8457 +#endif + +#ifndef GL_REND_screen_coordinates +#define GL_SCREEN_COORDINATES_REND 0x8490 +#define GL_INVERTED_SCREEN_W_REND 0x8491 +#endif + +#ifndef GL_EXT_coordinate_frame +#define GL_TANGENT_ARRAY_EXT 0x8439 +#define GL_BINORMAL_ARRAY_EXT 0x843A +#define GL_CURRENT_TANGENT_EXT 0x843B +#define GL_CURRENT_BINORMAL_EXT 0x843C +#define GL_TANGENT_ARRAY_TYPE_EXT 0x843E +#define GL_TANGENT_ARRAY_STRIDE_EXT 0x843F +#define GL_BINORMAL_ARRAY_TYPE_EXT 0x8440 +#define GL_BINORMAL_ARRAY_STRIDE_EXT 0x8441 +#define GL_TANGENT_ARRAY_POINTER_EXT 0x8442 +#define GL_BINORMAL_ARRAY_POINTER_EXT 0x8443 +#define GL_MAP1_TANGENT_EXT 0x8444 +#define GL_MAP2_TANGENT_EXT 0x8445 +#define GL_MAP1_BINORMAL_EXT 0x8446 +#define GL_MAP2_BINORMAL_EXT 0x8447 +#endif + +#ifndef GL_EXT_texture_env_combine +#define GL_COMBINE_EXT 0x8570 +#define GL_COMBINE_RGB_EXT 0x8571 +#define GL_COMBINE_ALPHA_EXT 0x8572 +#define GL_RGB_SCALE_EXT 0x8573 +#define GL_ADD_SIGNED_EXT 0x8574 +#define GL_INTERPOLATE_EXT 0x8575 +#define GL_CONSTANT_EXT 0x8576 +#define GL_PRIMARY_COLOR_EXT 0x8577 +#define GL_PREVIOUS_EXT 0x8578 +#define GL_SOURCE0_RGB_EXT 0x8580 +#define GL_SOURCE1_RGB_EXT 0x8581 +#define GL_SOURCE2_RGB_EXT 0x8582 +#define GL_SOURCE0_ALPHA_EXT 0x8588 +#define GL_SOURCE1_ALPHA_EXT 0x8589 +#define GL_SOURCE2_ALPHA_EXT 0x858A +#define GL_OPERAND0_RGB_EXT 0x8590 +#define GL_OPERAND1_RGB_EXT 0x8591 +#define GL_OPERAND2_RGB_EXT 0x8592 +#define GL_OPERAND0_ALPHA_EXT 0x8598 +#define GL_OPERAND1_ALPHA_EXT 0x8599 +#define GL_OPERAND2_ALPHA_EXT 0x859A +#endif + +#ifndef GL_APPLE_specular_vector +#define GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE 0x85B0 +#endif + +#ifndef GL_APPLE_transform_hint +#define GL_TRANSFORM_HINT_APPLE 0x85B1 +#endif + +#ifndef GL_SGIX_fog_scale +#define GL_FOG_SCALE_SGIX 0x81FC +#define GL_FOG_SCALE_VALUE_SGIX 0x81FD +#endif + +#ifndef GL_SUNX_constant_data +#define GL_UNPACK_CONSTANT_DATA_SUNX 0x81D5 +#define GL_TEXTURE_CONSTANT_DATA_SUNX 0x81D6 +#endif + +#ifndef GL_SUN_global_alpha +#define GL_GLOBAL_ALPHA_SUN 0x81D9 +#define GL_GLOBAL_ALPHA_FACTOR_SUN 0x81DA +#endif + +#ifndef GL_SUN_triangle_list +#define GL_RESTART_SUN 0x0001 +#define GL_REPLACE_MIDDLE_SUN 0x0002 +#define GL_REPLACE_OLDEST_SUN 0x0003 +#define GL_TRIANGLE_LIST_SUN 0x81D7 +#define GL_REPLACEMENT_CODE_SUN 0x81D8 +#define GL_REPLACEMENT_CODE_ARRAY_SUN 0x85C0 +#define GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN 0x85C1 +#define GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN 0x85C2 +#define GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN 0x85C3 +#define GL_R1UI_V3F_SUN 0x85C4 +#define GL_R1UI_C4UB_V3F_SUN 0x85C5 +#define GL_R1UI_C3F_V3F_SUN 0x85C6 +#define GL_R1UI_N3F_V3F_SUN 0x85C7 +#define GL_R1UI_C4F_N3F_V3F_SUN 0x85C8 +#define GL_R1UI_T2F_V3F_SUN 0x85C9 +#define GL_R1UI_T2F_N3F_V3F_SUN 0x85CA +#define GL_R1UI_T2F_C4F_N3F_V3F_SUN 0x85CB +#endif + +#ifndef GL_SUN_vertex +#endif + +#ifndef GL_EXT_blend_func_separate +#define GL_BLEND_DST_RGB_EXT 0x80C8 +#define GL_BLEND_SRC_RGB_EXT 0x80C9 +#define GL_BLEND_DST_ALPHA_EXT 0x80CA +#define GL_BLEND_SRC_ALPHA_EXT 0x80CB +#endif + +#ifndef GL_INGR_color_clamp +#define GL_RED_MIN_CLAMP_INGR 0x8560 +#define GL_GREEN_MIN_CLAMP_INGR 0x8561 +#define GL_BLUE_MIN_CLAMP_INGR 0x8562 +#define GL_ALPHA_MIN_CLAMP_INGR 0x8563 +#define GL_RED_MAX_CLAMP_INGR 0x8564 +#define GL_GREEN_MAX_CLAMP_INGR 0x8565 +#define GL_BLUE_MAX_CLAMP_INGR 0x8566 +#define GL_ALPHA_MAX_CLAMP_INGR 0x8567 +#endif + +#ifndef GL_INGR_interlace_read +#define GL_INTERLACE_READ_INGR 0x8568 +#endif + +#ifndef GL_EXT_stencil_wrap +#define GL_INCR_WRAP_EXT 0x8507 +#define GL_DECR_WRAP_EXT 0x8508 +#endif + +#ifndef GL_EXT_422_pixels +#define GL_422_EXT 0x80CC +#define GL_422_REV_EXT 0x80CD +#define GL_422_AVERAGE_EXT 0x80CE +#define GL_422_REV_AVERAGE_EXT 0x80CF +#endif + +#ifndef GL_NV_texgen_reflection +#define GL_NORMAL_MAP_NV 0x8511 +#define GL_REFLECTION_MAP_NV 0x8512 +#endif + +#ifndef GL_EXT_texture_cube_map +#define GL_NORMAL_MAP_EXT 0x8511 +#define GL_REFLECTION_MAP_EXT 0x8512 +#define GL_TEXTURE_CUBE_MAP_EXT 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_EXT 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_EXT 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT 0x851C +#endif + +#ifndef GL_SUN_convolution_border_modes +#define GL_WRAP_BORDER_SUN 0x81D4 +#endif + +#ifndef GL_EXT_texture_env_add +#endif + +#ifndef GL_EXT_texture_lod_bias +#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD +#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500 +#define GL_TEXTURE_LOD_BIAS_EXT 0x8501 +#endif + +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif + +#ifndef GL_EXT_vertex_weighting +#define GL_MODELVIEW0_STACK_DEPTH_EXT GL_MODELVIEW_STACK_DEPTH +#define GL_MODELVIEW1_STACK_DEPTH_EXT 0x8502 +#define GL_MODELVIEW0_MATRIX_EXT GL_MODELVIEW_MATRIX +#define GL_MODELVIEW1_MATRIX_EXT 0x8506 +#define GL_VERTEX_WEIGHTING_EXT 0x8509 +#define GL_MODELVIEW0_EXT GL_MODELVIEW +#define GL_MODELVIEW1_EXT 0x850A +#define GL_CURRENT_VERTEX_WEIGHT_EXT 0x850B +#define GL_VERTEX_WEIGHT_ARRAY_EXT 0x850C +#define GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT 0x850D +#define GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT 0x850E +#define GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT 0x850F +#define GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT 0x8510 +#endif + +#ifndef GL_NV_light_max_exponent +#define GL_MAX_SHININESS_NV 0x8504 +#define GL_MAX_SPOT_EXPONENT_NV 0x8505 +#endif + +#ifndef GL_NV_vertex_array_range +#define GL_VERTEX_ARRAY_RANGE_NV 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_NV 0x851E +#define GL_VERTEX_ARRAY_RANGE_VALID_NV 0x851F +#define GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV 0x8520 +#define GL_VERTEX_ARRAY_RANGE_POINTER_NV 0x8521 +#endif + +#ifndef GL_NV_register_combiners +#define GL_REGISTER_COMBINERS_NV 0x8522 +#define GL_VARIABLE_A_NV 0x8523 +#define GL_VARIABLE_B_NV 0x8524 +#define GL_VARIABLE_C_NV 0x8525 +#define GL_VARIABLE_D_NV 0x8526 +#define GL_VARIABLE_E_NV 0x8527 +#define GL_VARIABLE_F_NV 0x8528 +#define GL_VARIABLE_G_NV 0x8529 +#define GL_CONSTANT_COLOR0_NV 0x852A +#define GL_CONSTANT_COLOR1_NV 0x852B +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +#define GL_SPARE0_NV 0x852E +#define GL_SPARE1_NV 0x852F +#define GL_DISCARD_NV 0x8530 +#define GL_E_TIMES_F_NV 0x8531 +#define GL_SPARE0_PLUS_SECONDARY_COLOR_NV 0x8532 +#define GL_UNSIGNED_IDENTITY_NV 0x8536 +#define GL_UNSIGNED_INVERT_NV 0x8537 +#define GL_EXPAND_NORMAL_NV 0x8538 +#define GL_EXPAND_NEGATE_NV 0x8539 +#define GL_HALF_BIAS_NORMAL_NV 0x853A +#define GL_HALF_BIAS_NEGATE_NV 0x853B +#define GL_SIGNED_IDENTITY_NV 0x853C +#define GL_SIGNED_NEGATE_NV 0x853D +#define GL_SCALE_BY_TWO_NV 0x853E +#define GL_SCALE_BY_FOUR_NV 0x853F +#define GL_SCALE_BY_ONE_HALF_NV 0x8540 +#define GL_BIAS_BY_NEGATIVE_ONE_HALF_NV 0x8541 +#define GL_COMBINER_INPUT_NV 0x8542 +#define GL_COMBINER_MAPPING_NV 0x8543 +#define GL_COMBINER_COMPONENT_USAGE_NV 0x8544 +#define GL_COMBINER_AB_DOT_PRODUCT_NV 0x8545 +#define GL_COMBINER_CD_DOT_PRODUCT_NV 0x8546 +#define GL_COMBINER_MUX_SUM_NV 0x8547 +#define GL_COMBINER_SCALE_NV 0x8548 +#define GL_COMBINER_BIAS_NV 0x8549 +#define GL_COMBINER_AB_OUTPUT_NV 0x854A +#define GL_COMBINER_CD_OUTPUT_NV 0x854B +#define GL_COMBINER_SUM_OUTPUT_NV 0x854C +#define GL_MAX_GENERAL_COMBINERS_NV 0x854D +#define GL_NUM_GENERAL_COMBINERS_NV 0x854E +#define GL_COLOR_SUM_CLAMP_NV 0x854F +#define GL_COMBINER0_NV 0x8550 +#define GL_COMBINER1_NV 0x8551 +#define GL_COMBINER2_NV 0x8552 +#define GL_COMBINER3_NV 0x8553 +#define GL_COMBINER4_NV 0x8554 +#define GL_COMBINER5_NV 0x8555 +#define GL_COMBINER6_NV 0x8556 +#define GL_COMBINER7_NV 0x8557 +/* reuse GL_TEXTURE0_ARB */ +/* reuse GL_TEXTURE1_ARB */ +/* reuse GL_ZERO */ +/* reuse GL_NONE */ +/* reuse GL_FOG */ +#endif + +#ifndef GL_NV_fog_distance +#define GL_FOG_DISTANCE_MODE_NV 0x855A +#define GL_EYE_RADIAL_NV 0x855B +#define GL_EYE_PLANE_ABSOLUTE_NV 0x855C +/* reuse GL_EYE_PLANE */ +#endif + +#ifndef GL_NV_texgen_emboss +#define GL_EMBOSS_LIGHT_NV 0x855D +#define GL_EMBOSS_CONSTANT_NV 0x855E +#define GL_EMBOSS_MAP_NV 0x855F +#endif + +#ifndef GL_NV_blend_square +#endif + +#ifndef GL_NV_texture_env_combine4 +#define GL_COMBINE4_NV 0x8503 +#define GL_SOURCE3_RGB_NV 0x8583 +#define GL_SOURCE3_ALPHA_NV 0x858B +#define GL_OPERAND3_RGB_NV 0x8593 +#define GL_OPERAND3_ALPHA_NV 0x859B +#endif + +#ifndef GL_MESA_resize_buffers +#endif + +#ifndef GL_MESA_window_pos +#endif + +#ifndef GL_EXT_texture_compression_s3tc +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif + +#ifndef GL_IBM_cull_vertex +#define GL_CULL_VERTEX_IBM 103050 +#endif + +#ifndef GL_IBM_multimode_draw_arrays +#endif + +#ifndef GL_IBM_vertex_array_lists +#define GL_VERTEX_ARRAY_LIST_IBM 103070 +#define GL_NORMAL_ARRAY_LIST_IBM 103071 +#define GL_COLOR_ARRAY_LIST_IBM 103072 +#define GL_INDEX_ARRAY_LIST_IBM 103073 +#define GL_TEXTURE_COORD_ARRAY_LIST_IBM 103074 +#define GL_EDGE_FLAG_ARRAY_LIST_IBM 103075 +#define GL_FOG_COORDINATE_ARRAY_LIST_IBM 103076 +#define GL_SECONDARY_COLOR_ARRAY_LIST_IBM 103077 +#define GL_VERTEX_ARRAY_LIST_STRIDE_IBM 103080 +#define GL_NORMAL_ARRAY_LIST_STRIDE_IBM 103081 +#define GL_COLOR_ARRAY_LIST_STRIDE_IBM 103082 +#define GL_INDEX_ARRAY_LIST_STRIDE_IBM 103083 +#define GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM 103084 +#define GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM 103085 +#define GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM 103086 +#define GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM 103087 +#endif + +#ifndef GL_SGIX_subsample +#define GL_PACK_SUBSAMPLE_RATE_SGIX 0x85A0 +#define GL_UNPACK_SUBSAMPLE_RATE_SGIX 0x85A1 +#define GL_PIXEL_SUBSAMPLE_4444_SGIX 0x85A2 +#define GL_PIXEL_SUBSAMPLE_2424_SGIX 0x85A3 +#define GL_PIXEL_SUBSAMPLE_4242_SGIX 0x85A4 +#endif + +#ifndef GL_SGIX_ycrcb_subsample +#endif + +#ifndef GL_SGIX_ycrcba +#define GL_YCRCB_SGIX 0x8318 +#define GL_YCRCBA_SGIX 0x8319 +#endif + +#ifndef GL_SGI_depth_pass_instrument +#define GL_DEPTH_PASS_INSTRUMENT_SGIX 0x8310 +#define GL_DEPTH_PASS_INSTRUMENT_COUNTERS_SGIX 0x8311 +#define GL_DEPTH_PASS_INSTRUMENT_MAX_SGIX 0x8312 +#endif + +#ifndef GL_3DFX_texture_compression_FXT1 +#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1 +#endif + +#ifndef GL_3DFX_multisample +#define GL_MULTISAMPLE_3DFX 0x86B2 +#define GL_SAMPLE_BUFFERS_3DFX 0x86B3 +#define GL_SAMPLES_3DFX 0x86B4 +#define GL_MULTISAMPLE_BIT_3DFX 0x20000000 +#endif + +#ifndef GL_3DFX_tbuffer +#endif + +#ifndef GL_EXT_multisample +#define GL_MULTISAMPLE_EXT 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_EXT 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_EXT 0x809F +#define GL_SAMPLE_MASK_EXT 0x80A0 +#define GL_1PASS_EXT 0x80A1 +#define GL_2PASS_0_EXT 0x80A2 +#define GL_2PASS_1_EXT 0x80A3 +#define GL_4PASS_0_EXT 0x80A4 +#define GL_4PASS_1_EXT 0x80A5 +#define GL_4PASS_2_EXT 0x80A6 +#define GL_4PASS_3_EXT 0x80A7 +#define GL_SAMPLE_BUFFERS_EXT 0x80A8 +#define GL_SAMPLES_EXT 0x80A9 +#define GL_SAMPLE_MASK_VALUE_EXT 0x80AA +#define GL_SAMPLE_MASK_INVERT_EXT 0x80AB +#define GL_SAMPLE_PATTERN_EXT 0x80AC +#define GL_MULTISAMPLE_BIT_EXT 0x20000000 +#endif + +#ifndef GL_SGIX_vertex_preclip +#define GL_VERTEX_PRECLIP_SGIX 0x83EE +#define GL_VERTEX_PRECLIP_HINT_SGIX 0x83EF +#endif + +#ifndef GL_SGIX_convolution_accuracy +#define GL_CONVOLUTION_HINT_SGIX 0x8316 +#endif + +#ifndef GL_SGIX_resample +#define GL_PACK_RESAMPLE_SGIX 0x842C +#define GL_UNPACK_RESAMPLE_SGIX 0x842D +#define GL_RESAMPLE_REPLICATE_SGIX 0x842E +#define GL_RESAMPLE_ZERO_FILL_SGIX 0x842F +#define GL_RESAMPLE_DECIMATE_SGIX 0x8430 +#endif + +#ifndef GL_SGIS_point_line_texgen +#define GL_EYE_DISTANCE_TO_POINT_SGIS 0x81F0 +#define GL_OBJECT_DISTANCE_TO_POINT_SGIS 0x81F1 +#define GL_EYE_DISTANCE_TO_LINE_SGIS 0x81F2 +#define GL_OBJECT_DISTANCE_TO_LINE_SGIS 0x81F3 +#define GL_EYE_POINT_SGIS 0x81F4 +#define GL_OBJECT_POINT_SGIS 0x81F5 +#define GL_EYE_LINE_SGIS 0x81F6 +#define GL_OBJECT_LINE_SGIS 0x81F7 +#endif + +#ifndef GL_SGIS_texture_color_mask +#define GL_TEXTURE_COLOR_WRITEMASK_SGIS 0x81EF +#endif + +#ifndef GL_EXT_texture_env_dot3 +#define GL_DOT3_RGB_EXT 0x8740 +#define GL_DOT3_RGBA_EXT 0x8741 +#endif + +#ifndef GL_ATI_texture_mirror_once +#define GL_MIRROR_CLAMP_ATI 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_ATI 0x8743 +#endif + +#ifndef GL_NV_fence +#define GL_ALL_COMPLETED_NV 0x84F2 +#define GL_FENCE_STATUS_NV 0x84F3 +#define GL_FENCE_CONDITION_NV 0x84F4 +#endif + +#ifndef GL_IBM_texture_mirrored_repeat +#define GL_MIRRORED_REPEAT_IBM 0x8370 +#endif + +#ifndef GL_NV_evaluators +#define GL_EVAL_2D_NV 0x86C0 +#define GL_EVAL_TRIANGULAR_2D_NV 0x86C1 +#define GL_MAP_TESSELLATION_NV 0x86C2 +#define GL_MAP_ATTRIB_U_ORDER_NV 0x86C3 +#define GL_MAP_ATTRIB_V_ORDER_NV 0x86C4 +#define GL_EVAL_FRACTIONAL_TESSELLATION_NV 0x86C5 +#define GL_EVAL_VERTEX_ATTRIB0_NV 0x86C6 +#define GL_EVAL_VERTEX_ATTRIB1_NV 0x86C7 +#define GL_EVAL_VERTEX_ATTRIB2_NV 0x86C8 +#define GL_EVAL_VERTEX_ATTRIB3_NV 0x86C9 +#define GL_EVAL_VERTEX_ATTRIB4_NV 0x86CA +#define GL_EVAL_VERTEX_ATTRIB5_NV 0x86CB +#define GL_EVAL_VERTEX_ATTRIB6_NV 0x86CC +#define GL_EVAL_VERTEX_ATTRIB7_NV 0x86CD +#define GL_EVAL_VERTEX_ATTRIB8_NV 0x86CE +#define GL_EVAL_VERTEX_ATTRIB9_NV 0x86CF +#define GL_EVAL_VERTEX_ATTRIB10_NV 0x86D0 +#define GL_EVAL_VERTEX_ATTRIB11_NV 0x86D1 +#define GL_EVAL_VERTEX_ATTRIB12_NV 0x86D2 +#define GL_EVAL_VERTEX_ATTRIB13_NV 0x86D3 +#define GL_EVAL_VERTEX_ATTRIB14_NV 0x86D4 +#define GL_EVAL_VERTEX_ATTRIB15_NV 0x86D5 +#define GL_MAX_MAP_TESSELLATION_NV 0x86D6 +#define GL_MAX_RATIONAL_EVAL_ORDER_NV 0x86D7 +#endif + +#ifndef GL_NV_packed_depth_stencil +#define GL_DEPTH_STENCIL_NV 0x84F9 +#define GL_UNSIGNED_INT_24_8_NV 0x84FA +#endif + +#ifndef GL_NV_register_combiners2 +#define GL_PER_STAGE_CONSTANTS_NV 0x8535 +#endif + +#ifndef GL_NV_texture_compression_vtc +#endif + +#ifndef GL_NV_texture_rectangle +#define GL_TEXTURE_RECTANGLE_NV 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8 +#endif + +#ifndef GL_NV_texture_shader +#define GL_OFFSET_TEXTURE_RECTANGLE_NV 0x864C +#define GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV 0x864D +#define GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV 0x864E +#define GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV 0x86D9 +#define GL_UNSIGNED_INT_S8_S8_8_8_NV 0x86DA +#define GL_UNSIGNED_INT_8_8_S8_S8_REV_NV 0x86DB +#define GL_DSDT_MAG_INTENSITY_NV 0x86DC +#define GL_SHADER_CONSISTENT_NV 0x86DD +#define GL_TEXTURE_SHADER_NV 0x86DE +#define GL_SHADER_OPERATION_NV 0x86DF +#define GL_CULL_MODES_NV 0x86E0 +#define GL_OFFSET_TEXTURE_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_BIAS_NV 0x86E3 +#define GL_OFFSET_TEXTURE_2D_MATRIX_NV GL_OFFSET_TEXTURE_MATRIX_NV +#define GL_OFFSET_TEXTURE_2D_SCALE_NV GL_OFFSET_TEXTURE_SCALE_NV +#define GL_OFFSET_TEXTURE_2D_BIAS_NV GL_OFFSET_TEXTURE_BIAS_NV +#define GL_PREVIOUS_TEXTURE_INPUT_NV 0x86E4 +#define GL_CONST_EYE_NV 0x86E5 +#define GL_PASS_THROUGH_NV 0x86E6 +#define GL_CULL_FRAGMENT_NV 0x86E7 +#define GL_OFFSET_TEXTURE_2D_NV 0x86E8 +#define GL_DEPENDENT_AR_TEXTURE_2D_NV 0x86E9 +#define GL_DEPENDENT_GB_TEXTURE_2D_NV 0x86EA +#define GL_DOT_PRODUCT_NV 0x86EC +#define GL_DOT_PRODUCT_DEPTH_REPLACE_NV 0x86ED +#define GL_DOT_PRODUCT_TEXTURE_2D_NV 0x86EE +#define GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV 0x86F0 +#define GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV 0x86F1 +#define GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV 0x86F2 +#define GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV 0x86F3 +#define GL_HILO_NV 0x86F4 +#define GL_DSDT_NV 0x86F5 +#define GL_DSDT_MAG_NV 0x86F6 +#define GL_DSDT_MAG_VIB_NV 0x86F7 +#define GL_HILO16_NV 0x86F8 +#define GL_SIGNED_HILO_NV 0x86F9 +#define GL_SIGNED_HILO16_NV 0x86FA +#define GL_SIGNED_RGBA_NV 0x86FB +#define GL_SIGNED_RGBA8_NV 0x86FC +#define GL_SIGNED_RGB_NV 0x86FE +#define GL_SIGNED_RGB8_NV 0x86FF +#define GL_SIGNED_LUMINANCE_NV 0x8701 +#define GL_SIGNED_LUMINANCE8_NV 0x8702 +#define GL_SIGNED_LUMINANCE_ALPHA_NV 0x8703 +#define GL_SIGNED_LUMINANCE8_ALPHA8_NV 0x8704 +#define GL_SIGNED_ALPHA_NV 0x8705 +#define GL_SIGNED_ALPHA8_NV 0x8706 +#define GL_SIGNED_INTENSITY_NV 0x8707 +#define GL_SIGNED_INTENSITY8_NV 0x8708 +#define GL_DSDT8_NV 0x8709 +#define GL_DSDT8_MAG8_NV 0x870A +#define GL_DSDT8_MAG8_INTENSITY8_NV 0x870B +#define GL_SIGNED_RGB_UNSIGNED_ALPHA_NV 0x870C +#define GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV 0x870D +#define GL_HI_SCALE_NV 0x870E +#define GL_LO_SCALE_NV 0x870F +#define GL_DS_SCALE_NV 0x8710 +#define GL_DT_SCALE_NV 0x8711 +#define GL_MAGNITUDE_SCALE_NV 0x8712 +#define GL_VIBRANCE_SCALE_NV 0x8713 +#define GL_HI_BIAS_NV 0x8714 +#define GL_LO_BIAS_NV 0x8715 +#define GL_DS_BIAS_NV 0x8716 +#define GL_DT_BIAS_NV 0x8717 +#define GL_MAGNITUDE_BIAS_NV 0x8718 +#define GL_VIBRANCE_BIAS_NV 0x8719 +#define GL_TEXTURE_BORDER_VALUES_NV 0x871A +#define GL_TEXTURE_HI_SIZE_NV 0x871B +#define GL_TEXTURE_LO_SIZE_NV 0x871C +#define GL_TEXTURE_DS_SIZE_NV 0x871D +#define GL_TEXTURE_DT_SIZE_NV 0x871E +#define GL_TEXTURE_MAG_SIZE_NV 0x871F +#endif + +#ifndef GL_NV_texture_shader2 +#define GL_DOT_PRODUCT_TEXTURE_3D_NV 0x86EF +#endif + +#ifndef GL_NV_vertex_array_range2 +#define GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV 0x8533 +#endif + +#ifndef GL_NV_vertex_program +#define GL_VERTEX_PROGRAM_NV 0x8620 +#define GL_VERTEX_STATE_PROGRAM_NV 0x8621 +#define GL_ATTRIB_ARRAY_SIZE_NV 0x8623 +#define GL_ATTRIB_ARRAY_STRIDE_NV 0x8624 +#define GL_ATTRIB_ARRAY_TYPE_NV 0x8625 +#define GL_CURRENT_ATTRIB_NV 0x8626 +#define GL_PROGRAM_LENGTH_NV 0x8627 +#define GL_PROGRAM_STRING_NV 0x8628 +#define GL_MODELVIEW_PROJECTION_NV 0x8629 +#define GL_IDENTITY_NV 0x862A +#define GL_INVERSE_NV 0x862B +#define GL_TRANSPOSE_NV 0x862C +#define GL_INVERSE_TRANSPOSE_NV 0x862D +#define GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV 0x862E +#define GL_MAX_TRACK_MATRICES_NV 0x862F +#define GL_MATRIX0_NV 0x8630 +#define GL_MATRIX1_NV 0x8631 +#define GL_MATRIX2_NV 0x8632 +#define GL_MATRIX3_NV 0x8633 +#define GL_MATRIX4_NV 0x8634 +#define GL_MATRIX5_NV 0x8635 +#define GL_MATRIX6_NV 0x8636 +#define GL_MATRIX7_NV 0x8637 +#define GL_CURRENT_MATRIX_STACK_DEPTH_NV 0x8640 +#define GL_CURRENT_MATRIX_NV 0x8641 +#define GL_VERTEX_PROGRAM_POINT_SIZE_NV 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_NV 0x8643 +#define GL_PROGRAM_PARAMETER_NV 0x8644 +#define GL_ATTRIB_ARRAY_POINTER_NV 0x8645 +#define GL_PROGRAM_TARGET_NV 0x8646 +#define GL_PROGRAM_RESIDENT_NV 0x8647 +#define GL_TRACK_MATRIX_NV 0x8648 +#define GL_TRACK_MATRIX_TRANSFORM_NV 0x8649 +#define GL_VERTEX_PROGRAM_BINDING_NV 0x864A +#define GL_PROGRAM_ERROR_POSITION_NV 0x864B +#define GL_VERTEX_ATTRIB_ARRAY0_NV 0x8650 +#define GL_VERTEX_ATTRIB_ARRAY1_NV 0x8651 +#define GL_VERTEX_ATTRIB_ARRAY2_NV 0x8652 +#define GL_VERTEX_ATTRIB_ARRAY3_NV 0x8653 +#define GL_VERTEX_ATTRIB_ARRAY4_NV 0x8654 +#define GL_VERTEX_ATTRIB_ARRAY5_NV 0x8655 +#define GL_VERTEX_ATTRIB_ARRAY6_NV 0x8656 +#define GL_VERTEX_ATTRIB_ARRAY7_NV 0x8657 +#define GL_VERTEX_ATTRIB_ARRAY8_NV 0x8658 +#define GL_VERTEX_ATTRIB_ARRAY9_NV 0x8659 +#define GL_VERTEX_ATTRIB_ARRAY10_NV 0x865A +#define GL_VERTEX_ATTRIB_ARRAY11_NV 0x865B +#define GL_VERTEX_ATTRIB_ARRAY12_NV 0x865C +#define GL_VERTEX_ATTRIB_ARRAY13_NV 0x865D +#define GL_VERTEX_ATTRIB_ARRAY14_NV 0x865E +#define GL_VERTEX_ATTRIB_ARRAY15_NV 0x865F +#define GL_MAP1_VERTEX_ATTRIB0_4_NV 0x8660 +#define GL_MAP1_VERTEX_ATTRIB1_4_NV 0x8661 +#define GL_MAP1_VERTEX_ATTRIB2_4_NV 0x8662 +#define GL_MAP1_VERTEX_ATTRIB3_4_NV 0x8663 +#define GL_MAP1_VERTEX_ATTRIB4_4_NV 0x8664 +#define GL_MAP1_VERTEX_ATTRIB5_4_NV 0x8665 +#define GL_MAP1_VERTEX_ATTRIB6_4_NV 0x8666 +#define GL_MAP1_VERTEX_ATTRIB7_4_NV 0x8667 +#define GL_MAP1_VERTEX_ATTRIB8_4_NV 0x8668 +#define GL_MAP1_VERTEX_ATTRIB9_4_NV 0x8669 +#define GL_MAP1_VERTEX_ATTRIB10_4_NV 0x866A +#define GL_MAP1_VERTEX_ATTRIB11_4_NV 0x866B +#define GL_MAP1_VERTEX_ATTRIB12_4_NV 0x866C +#define GL_MAP1_VERTEX_ATTRIB13_4_NV 0x866D +#define GL_MAP1_VERTEX_ATTRIB14_4_NV 0x866E +#define GL_MAP1_VERTEX_ATTRIB15_4_NV 0x866F +#define GL_MAP2_VERTEX_ATTRIB0_4_NV 0x8670 +#define GL_MAP2_VERTEX_ATTRIB1_4_NV 0x8671 +#define GL_MAP2_VERTEX_ATTRIB2_4_NV 0x8672 +#define GL_MAP2_VERTEX_ATTRIB3_4_NV 0x8673 +#define GL_MAP2_VERTEX_ATTRIB4_4_NV 0x8674 +#define GL_MAP2_VERTEX_ATTRIB5_4_NV 0x8675 +#define GL_MAP2_VERTEX_ATTRIB6_4_NV 0x8676 +#define GL_MAP2_VERTEX_ATTRIB7_4_NV 0x8677 +#define GL_MAP2_VERTEX_ATTRIB8_4_NV 0x8678 +#define GL_MAP2_VERTEX_ATTRIB9_4_NV 0x8679 +#define GL_MAP2_VERTEX_ATTRIB10_4_NV 0x867A +#define GL_MAP2_VERTEX_ATTRIB11_4_NV 0x867B +#define GL_MAP2_VERTEX_ATTRIB12_4_NV 0x867C +#define GL_MAP2_VERTEX_ATTRIB13_4_NV 0x867D +#define GL_MAP2_VERTEX_ATTRIB14_4_NV 0x867E +#define GL_MAP2_VERTEX_ATTRIB15_4_NV 0x867F +#endif + +#ifndef GL_SGIX_texture_coordinate_clamp +#define GL_TEXTURE_MAX_CLAMP_S_SGIX 0x8369 +#define GL_TEXTURE_MAX_CLAMP_T_SGIX 0x836A +#define GL_TEXTURE_MAX_CLAMP_R_SGIX 0x836B +#endif + +#ifndef GL_SGIX_scalebias_hint +#define GL_SCALEBIAS_HINT_SGIX 0x8322 +#endif + +#ifndef GL_OML_interlace +#define GL_INTERLACE_OML 0x8980 +#define GL_INTERLACE_READ_OML 0x8981 +#endif + +#ifndef GL_OML_subsample +#define GL_FORMAT_SUBSAMPLE_24_24_OML 0x8982 +#define GL_FORMAT_SUBSAMPLE_244_244_OML 0x8983 +#endif + +#ifndef GL_OML_resample +#define GL_PACK_RESAMPLE_OML 0x8984 +#define GL_UNPACK_RESAMPLE_OML 0x8985 +#define GL_RESAMPLE_REPLICATE_OML 0x8986 +#define GL_RESAMPLE_ZERO_FILL_OML 0x8987 +#define GL_RESAMPLE_AVERAGE_OML 0x8988 +#define GL_RESAMPLE_DECIMATE_OML 0x8989 +#endif + +#ifndef GL_NV_copy_depth_to_color +#define GL_DEPTH_STENCIL_TO_RGBA_NV 0x886E +#define GL_DEPTH_STENCIL_TO_BGRA_NV 0x886F +#endif + +#ifndef GL_ATI_envmap_bumpmap +#define GL_BUMP_ROT_MATRIX_ATI 0x8775 +#define GL_BUMP_ROT_MATRIX_SIZE_ATI 0x8776 +#define GL_BUMP_NUM_TEX_UNITS_ATI 0x8777 +#define GL_BUMP_TEX_UNITS_ATI 0x8778 +#define GL_DUDV_ATI 0x8779 +#define GL_DU8DV8_ATI 0x877A +#define GL_BUMP_ENVMAP_ATI 0x877B +#define GL_BUMP_TARGET_ATI 0x877C +#endif + +#ifndef GL_ATI_fragment_shader +#define GL_FRAGMENT_SHADER_ATI 0x8920 +#define GL_REG_0_ATI 0x8921 +#define GL_REG_1_ATI 0x8922 +#define GL_REG_2_ATI 0x8923 +#define GL_REG_3_ATI 0x8924 +#define GL_REG_4_ATI 0x8925 +#define GL_REG_5_ATI 0x8926 +#define GL_REG_6_ATI 0x8927 +#define GL_REG_7_ATI 0x8928 +#define GL_REG_8_ATI 0x8929 +#define GL_REG_9_ATI 0x892A +#define GL_REG_10_ATI 0x892B +#define GL_REG_11_ATI 0x892C +#define GL_REG_12_ATI 0x892D +#define GL_REG_13_ATI 0x892E +#define GL_REG_14_ATI 0x892F +#define GL_REG_15_ATI 0x8930 +#define GL_REG_16_ATI 0x8931 +#define GL_REG_17_ATI 0x8932 +#define GL_REG_18_ATI 0x8933 +#define GL_REG_19_ATI 0x8934 +#define GL_REG_20_ATI 0x8935 +#define GL_REG_21_ATI 0x8936 +#define GL_REG_22_ATI 0x8937 +#define GL_REG_23_ATI 0x8938 +#define GL_REG_24_ATI 0x8939 +#define GL_REG_25_ATI 0x893A +#define GL_REG_26_ATI 0x893B +#define GL_REG_27_ATI 0x893C +#define GL_REG_28_ATI 0x893D +#define GL_REG_29_ATI 0x893E +#define GL_REG_30_ATI 0x893F +#define GL_REG_31_ATI 0x8940 +#define GL_CON_0_ATI 0x8941 +#define GL_CON_1_ATI 0x8942 +#define GL_CON_2_ATI 0x8943 +#define GL_CON_3_ATI 0x8944 +#define GL_CON_4_ATI 0x8945 +#define GL_CON_5_ATI 0x8946 +#define GL_CON_6_ATI 0x8947 +#define GL_CON_7_ATI 0x8948 +#define GL_CON_8_ATI 0x8949 +#define GL_CON_9_ATI 0x894A +#define GL_CON_10_ATI 0x894B +#define GL_CON_11_ATI 0x894C +#define GL_CON_12_ATI 0x894D +#define GL_CON_13_ATI 0x894E +#define GL_CON_14_ATI 0x894F +#define GL_CON_15_ATI 0x8950 +#define GL_CON_16_ATI 0x8951 +#define GL_CON_17_ATI 0x8952 +#define GL_CON_18_ATI 0x8953 +#define GL_CON_19_ATI 0x8954 +#define GL_CON_20_ATI 0x8955 +#define GL_CON_21_ATI 0x8956 +#define GL_CON_22_ATI 0x8957 +#define GL_CON_23_ATI 0x8958 +#define GL_CON_24_ATI 0x8959 +#define GL_CON_25_ATI 0x895A +#define GL_CON_26_ATI 0x895B +#define GL_CON_27_ATI 0x895C +#define GL_CON_28_ATI 0x895D +#define GL_CON_29_ATI 0x895E +#define GL_CON_30_ATI 0x895F +#define GL_CON_31_ATI 0x8960 +#define GL_MOV_ATI 0x8961 +#define GL_ADD_ATI 0x8963 +#define GL_MUL_ATI 0x8964 +#define GL_SUB_ATI 0x8965 +#define GL_DOT3_ATI 0x8966 +#define GL_DOT4_ATI 0x8967 +#define GL_MAD_ATI 0x8968 +#define GL_LERP_ATI 0x8969 +#define GL_CND_ATI 0x896A +#define GL_CND0_ATI 0x896B +#define GL_DOT2_ADD_ATI 0x896C +#define GL_SECONDARY_INTERPOLATOR_ATI 0x896D +#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E +#define GL_NUM_FRAGMENT_CONSTANTS_ATI 0x896F +#define GL_NUM_PASSES_ATI 0x8970 +#define GL_NUM_INSTRUCTIONS_PER_PASS_ATI 0x8971 +#define GL_NUM_INSTRUCTIONS_TOTAL_ATI 0x8972 +#define GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI 0x8973 +#define GL_NUM_LOOPBACK_COMPONENTS_ATI 0x8974 +#define GL_COLOR_ALPHA_PAIRING_ATI 0x8975 +#define GL_SWIZZLE_STR_ATI 0x8976 +#define GL_SWIZZLE_STQ_ATI 0x8977 +#define GL_SWIZZLE_STR_DR_ATI 0x8978 +#define GL_SWIZZLE_STQ_DQ_ATI 0x8979 +#define GL_SWIZZLE_STRQ_ATI 0x897A +#define GL_SWIZZLE_STRQ_DQ_ATI 0x897B +#define GL_RED_BIT_ATI 0x00000001 +#define GL_GREEN_BIT_ATI 0x00000002 +#define GL_BLUE_BIT_ATI 0x00000004 +#define GL_2X_BIT_ATI 0x00000001 +#define GL_4X_BIT_ATI 0x00000002 +#define GL_8X_BIT_ATI 0x00000004 +#define GL_HALF_BIT_ATI 0x00000008 +#define GL_QUARTER_BIT_ATI 0x00000010 +#define GL_EIGHTH_BIT_ATI 0x00000020 +#define GL_SATURATE_BIT_ATI 0x00000040 +#define GL_COMP_BIT_ATI 0x00000002 +#define GL_NEGATE_BIT_ATI 0x00000004 +#define GL_BIAS_BIT_ATI 0x00000008 +#endif + +#ifndef GL_ATI_pn_triangles +#define GL_PN_TRIANGLES_ATI 0x87F0 +#define GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F1 +#define GL_PN_TRIANGLES_POINT_MODE_ATI 0x87F2 +#define GL_PN_TRIANGLES_NORMAL_MODE_ATI 0x87F3 +#define GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F4 +#define GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI 0x87F5 +#define GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI 0x87F6 +#define GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI 0x87F7 +#define GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI 0x87F8 +#endif + +#ifndef GL_ATI_vertex_array_object +#define GL_STATIC_ATI 0x8760 +#define GL_DYNAMIC_ATI 0x8761 +#define GL_PRESERVE_ATI 0x8762 +#define GL_DISCARD_ATI 0x8763 +#define GL_OBJECT_BUFFER_SIZE_ATI 0x8764 +#define GL_OBJECT_BUFFER_USAGE_ATI 0x8765 +#define GL_ARRAY_OBJECT_BUFFER_ATI 0x8766 +#define GL_ARRAY_OBJECT_OFFSET_ATI 0x8767 +#endif + +#ifndef GL_EXT_vertex_shader +#define GL_VERTEX_SHADER_EXT 0x8780 +#define GL_VERTEX_SHADER_BINDING_EXT 0x8781 +#define GL_OP_INDEX_EXT 0x8782 +#define GL_OP_NEGATE_EXT 0x8783 +#define GL_OP_DOT3_EXT 0x8784 +#define GL_OP_DOT4_EXT 0x8785 +#define GL_OP_MUL_EXT 0x8786 +#define GL_OP_ADD_EXT 0x8787 +#define GL_OP_MADD_EXT 0x8788 +#define GL_OP_FRAC_EXT 0x8789 +#define GL_OP_MAX_EXT 0x878A +#define GL_OP_MIN_EXT 0x878B +#define GL_OP_SET_GE_EXT 0x878C +#define GL_OP_SET_LT_EXT 0x878D +#define GL_OP_CLAMP_EXT 0x878E +#define GL_OP_FLOOR_EXT 0x878F +#define GL_OP_ROUND_EXT 0x8790 +#define GL_OP_EXP_BASE_2_EXT 0x8791 +#define GL_OP_LOG_BASE_2_EXT 0x8792 +#define GL_OP_POWER_EXT 0x8793 +#define GL_OP_RECIP_EXT 0x8794 +#define GL_OP_RECIP_SQRT_EXT 0x8795 +#define GL_OP_SUB_EXT 0x8796 +#define GL_OP_CROSS_PRODUCT_EXT 0x8797 +#define GL_OP_MULTIPLY_MATRIX_EXT 0x8798 +#define GL_OP_MOV_EXT 0x8799 +#define GL_OUTPUT_VERTEX_EXT 0x879A +#define GL_OUTPUT_COLOR0_EXT 0x879B +#define GL_OUTPUT_COLOR1_EXT 0x879C +#define GL_OUTPUT_TEXTURE_COORD0_EXT 0x879D +#define GL_OUTPUT_TEXTURE_COORD1_EXT 0x879E +#define GL_OUTPUT_TEXTURE_COORD2_EXT 0x879F +#define GL_OUTPUT_TEXTURE_COORD3_EXT 0x87A0 +#define GL_OUTPUT_TEXTURE_COORD4_EXT 0x87A1 +#define GL_OUTPUT_TEXTURE_COORD5_EXT 0x87A2 +#define GL_OUTPUT_TEXTURE_COORD6_EXT 0x87A3 +#define GL_OUTPUT_TEXTURE_COORD7_EXT 0x87A4 +#define GL_OUTPUT_TEXTURE_COORD8_EXT 0x87A5 +#define GL_OUTPUT_TEXTURE_COORD9_EXT 0x87A6 +#define GL_OUTPUT_TEXTURE_COORD10_EXT 0x87A7 +#define GL_OUTPUT_TEXTURE_COORD11_EXT 0x87A8 +#define GL_OUTPUT_TEXTURE_COORD12_EXT 0x87A9 +#define GL_OUTPUT_TEXTURE_COORD13_EXT 0x87AA +#define GL_OUTPUT_TEXTURE_COORD14_EXT 0x87AB +#define GL_OUTPUT_TEXTURE_COORD15_EXT 0x87AC +#define GL_OUTPUT_TEXTURE_COORD16_EXT 0x87AD +#define GL_OUTPUT_TEXTURE_COORD17_EXT 0x87AE +#define GL_OUTPUT_TEXTURE_COORD18_EXT 0x87AF +#define GL_OUTPUT_TEXTURE_COORD19_EXT 0x87B0 +#define GL_OUTPUT_TEXTURE_COORD20_EXT 0x87B1 +#define GL_OUTPUT_TEXTURE_COORD21_EXT 0x87B2 +#define GL_OUTPUT_TEXTURE_COORD22_EXT 0x87B3 +#define GL_OUTPUT_TEXTURE_COORD23_EXT 0x87B4 +#define GL_OUTPUT_TEXTURE_COORD24_EXT 0x87B5 +#define GL_OUTPUT_TEXTURE_COORD25_EXT 0x87B6 +#define GL_OUTPUT_TEXTURE_COORD26_EXT 0x87B7 +#define GL_OUTPUT_TEXTURE_COORD27_EXT 0x87B8 +#define GL_OUTPUT_TEXTURE_COORD28_EXT 0x87B9 +#define GL_OUTPUT_TEXTURE_COORD29_EXT 0x87BA +#define GL_OUTPUT_TEXTURE_COORD30_EXT 0x87BB +#define GL_OUTPUT_TEXTURE_COORD31_EXT 0x87BC +#define GL_OUTPUT_FOG_EXT 0x87BD +#define GL_SCALAR_EXT 0x87BE +#define GL_VECTOR_EXT 0x87BF +#define GL_MATRIX_EXT 0x87C0 +#define GL_VARIANT_EXT 0x87C1 +#define GL_INVARIANT_EXT 0x87C2 +#define GL_LOCAL_CONSTANT_EXT 0x87C3 +#define GL_LOCAL_EXT 0x87C4 +#define GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87C5 +#define GL_MAX_VERTEX_SHADER_VARIANTS_EXT 0x87C6 +#define GL_MAX_VERTEX_SHADER_INVARIANTS_EXT 0x87C7 +#define GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87C8 +#define GL_MAX_VERTEX_SHADER_LOCALS_EXT 0x87C9 +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CA +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT 0x87CB +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87CC +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT 0x87CD +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT 0x87CE +#define GL_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CF +#define GL_VERTEX_SHADER_VARIANTS_EXT 0x87D0 +#define GL_VERTEX_SHADER_INVARIANTS_EXT 0x87D1 +#define GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87D2 +#define GL_VERTEX_SHADER_LOCALS_EXT 0x87D3 +#define GL_VERTEX_SHADER_OPTIMIZED_EXT 0x87D4 +#define GL_X_EXT 0x87D5 +#define GL_Y_EXT 0x87D6 +#define GL_Z_EXT 0x87D7 +#define GL_W_EXT 0x87D8 +#define GL_NEGATIVE_X_EXT 0x87D9 +#define GL_NEGATIVE_Y_EXT 0x87DA +#define GL_NEGATIVE_Z_EXT 0x87DB +#define GL_NEGATIVE_W_EXT 0x87DC +#define GL_ZERO_EXT 0x87DD +#define GL_ONE_EXT 0x87DE +#define GL_NEGATIVE_ONE_EXT 0x87DF +#define GL_NORMALIZED_RANGE_EXT 0x87E0 +#define GL_FULL_RANGE_EXT 0x87E1 +#define GL_CURRENT_VERTEX_EXT 0x87E2 +#define GL_MVP_MATRIX_EXT 0x87E3 +#define GL_VARIANT_VALUE_EXT 0x87E4 +#define GL_VARIANT_DATATYPE_EXT 0x87E5 +#define GL_VARIANT_ARRAY_STRIDE_EXT 0x87E6 +#define GL_VARIANT_ARRAY_TYPE_EXT 0x87E7 +#define GL_VARIANT_ARRAY_EXT 0x87E8 +#define GL_VARIANT_ARRAY_POINTER_EXT 0x87E9 +#define GL_INVARIANT_VALUE_EXT 0x87EA +#define GL_INVARIANT_DATATYPE_EXT 0x87EB +#define GL_LOCAL_CONSTANT_VALUE_EXT 0x87EC +#define GL_LOCAL_CONSTANT_DATATYPE_EXT 0x87ED +#endif + +#ifndef GL_ATI_vertex_streams +#define GL_MAX_VERTEX_STREAMS_ATI 0x876B +#define GL_VERTEX_STREAM0_ATI 0x876C +#define GL_VERTEX_STREAM1_ATI 0x876D +#define GL_VERTEX_STREAM2_ATI 0x876E +#define GL_VERTEX_STREAM3_ATI 0x876F +#define GL_VERTEX_STREAM4_ATI 0x8770 +#define GL_VERTEX_STREAM5_ATI 0x8771 +#define GL_VERTEX_STREAM6_ATI 0x8772 +#define GL_VERTEX_STREAM7_ATI 0x8773 +#define GL_VERTEX_SOURCE_ATI 0x8774 +#endif + +#ifndef GL_ATI_element_array +#define GL_ELEMENT_ARRAY_ATI 0x8768 +#define GL_ELEMENT_ARRAY_TYPE_ATI 0x8769 +#define GL_ELEMENT_ARRAY_POINTER_ATI 0x876A +#endif + +#ifndef GL_SUN_mesh_array +#define GL_QUAD_MESH_SUN 0x8614 +#define GL_TRIANGLE_MESH_SUN 0x8615 +#endif + +#ifndef GL_SUN_slice_accum +#define GL_SLICE_ACCUM_SUN 0x85CC +#endif + +#ifndef GL_NV_multisample_filter_hint +#define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534 +#endif + +#ifndef GL_NV_depth_clamp +#define GL_DEPTH_CLAMP_NV 0x864F +#endif + +#ifndef GL_NV_occlusion_query +#define GL_PIXEL_COUNTER_BITS_NV 0x8864 +#define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865 +#define GL_PIXEL_COUNT_NV 0x8866 +#define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867 +#endif + +#ifndef GL_NV_point_sprite +#define GL_POINT_SPRITE_NV 0x8861 +#define GL_COORD_REPLACE_NV 0x8862 +#define GL_POINT_SPRITE_R_MODE_NV 0x8863 +#endif + +#ifndef GL_NV_texture_shader3 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV 0x8850 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV 0x8851 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8852 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV 0x8853 +#define GL_OFFSET_HILO_TEXTURE_2D_NV 0x8854 +#define GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV 0x8855 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV 0x8856 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8857 +#define GL_DEPENDENT_HILO_TEXTURE_2D_NV 0x8858 +#define GL_DEPENDENT_RGB_TEXTURE_3D_NV 0x8859 +#define GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV 0x885A +#define GL_DOT_PRODUCT_PASS_THROUGH_NV 0x885B +#define GL_DOT_PRODUCT_TEXTURE_1D_NV 0x885C +#define GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV 0x885D +#define GL_HILO8_NV 0x885E +#define GL_SIGNED_HILO8_NV 0x885F +#define GL_FORCE_BLUE_TO_ONE_NV 0x8860 +#endif + +#ifndef GL_NV_vertex_program1_1 +#endif + +#ifndef GL_EXT_shadow_funcs +#endif + +#ifndef GL_EXT_stencil_two_side +#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910 +#define GL_ACTIVE_STENCIL_FACE_EXT 0x8911 +#endif + +#ifndef GL_ATI_text_fragment_shader +#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200 +#endif + +#ifndef GL_APPLE_client_storage +#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2 +#endif + +#ifndef GL_APPLE_element_array +#define GL_ELEMENT_ARRAY_APPLE 0x8768 +#define GL_ELEMENT_ARRAY_TYPE_APPLE 0x8769 +#define GL_ELEMENT_ARRAY_POINTER_APPLE 0x876A +#endif + +#ifndef GL_APPLE_fence +#define GL_DRAW_PIXELS_APPLE 0x8A0A +#define GL_FENCE_APPLE 0x8A0B +#endif + +#ifndef GL_APPLE_vertex_array_object +#define GL_VERTEX_ARRAY_BINDING_APPLE 0x85B5 +#endif + +#ifndef GL_APPLE_vertex_array_range +#define GL_VERTEX_ARRAY_RANGE_APPLE 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE 0x851E +#define GL_VERTEX_ARRAY_STORAGE_HINT_APPLE 0x851F +#define GL_VERTEX_ARRAY_RANGE_POINTER_APPLE 0x8521 +#define GL_STORAGE_CACHED_APPLE 0x85BE +#define GL_STORAGE_SHARED_APPLE 0x85BF +#endif + +#ifndef GL_APPLE_ycbcr_422 +#define GL_YCBCR_422_APPLE 0x85B9 +#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB +#endif + +#ifndef GL_S3_s3tc +#define GL_RGB_S3TC 0x83A0 +#define GL_RGB4_S3TC 0x83A1 +#define GL_RGBA_S3TC 0x83A2 +#define GL_RGBA4_S3TC 0x83A3 +#endif + +#ifndef GL_ATI_draw_buffers +#define GL_MAX_DRAW_BUFFERS_ATI 0x8824 +#define GL_DRAW_BUFFER0_ATI 0x8825 +#define GL_DRAW_BUFFER1_ATI 0x8826 +#define GL_DRAW_BUFFER2_ATI 0x8827 +#define GL_DRAW_BUFFER3_ATI 0x8828 +#define GL_DRAW_BUFFER4_ATI 0x8829 +#define GL_DRAW_BUFFER5_ATI 0x882A +#define GL_DRAW_BUFFER6_ATI 0x882B +#define GL_DRAW_BUFFER7_ATI 0x882C +#define GL_DRAW_BUFFER8_ATI 0x882D +#define GL_DRAW_BUFFER9_ATI 0x882E +#define GL_DRAW_BUFFER10_ATI 0x882F +#define GL_DRAW_BUFFER11_ATI 0x8830 +#define GL_DRAW_BUFFER12_ATI 0x8831 +#define GL_DRAW_BUFFER13_ATI 0x8832 +#define GL_DRAW_BUFFER14_ATI 0x8833 +#define GL_DRAW_BUFFER15_ATI 0x8834 +#endif + +#ifndef GL_ATI_pixel_format_float +#define GL_TYPE_RGBA_FLOAT_ATI 0x8820 +#define GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI 0x8835 +#endif + +#ifndef GL_ATI_texture_env_combine3 +#define GL_MODULATE_ADD_ATI 0x8744 +#define GL_MODULATE_SIGNED_ADD_ATI 0x8745 +#define GL_MODULATE_SUBTRACT_ATI 0x8746 +#endif + +#ifndef GL_ATI_texture_float +#define GL_RGBA_FLOAT32_ATI 0x8814 +#define GL_RGB_FLOAT32_ATI 0x8815 +#define GL_ALPHA_FLOAT32_ATI 0x8816 +#define GL_INTENSITY_FLOAT32_ATI 0x8817 +#define GL_LUMINANCE_FLOAT32_ATI 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_ATI 0x8819 +#define GL_RGBA_FLOAT16_ATI 0x881A +#define GL_RGB_FLOAT16_ATI 0x881B +#define GL_ALPHA_FLOAT16_ATI 0x881C +#define GL_INTENSITY_FLOAT16_ATI 0x881D +#define GL_LUMINANCE_FLOAT16_ATI 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_ATI 0x881F +#endif + +#ifndef GL_NV_float_buffer +#define GL_FLOAT_R_NV 0x8880 +#define GL_FLOAT_RG_NV 0x8881 +#define GL_FLOAT_RGB_NV 0x8882 +#define GL_FLOAT_RGBA_NV 0x8883 +#define GL_FLOAT_R16_NV 0x8884 +#define GL_FLOAT_R32_NV 0x8885 +#define GL_FLOAT_RG16_NV 0x8886 +#define GL_FLOAT_RG32_NV 0x8887 +#define GL_FLOAT_RGB16_NV 0x8888 +#define GL_FLOAT_RGB32_NV 0x8889 +#define GL_FLOAT_RGBA16_NV 0x888A +#define GL_FLOAT_RGBA32_NV 0x888B +#define GL_TEXTURE_FLOAT_COMPONENTS_NV 0x888C +#define GL_FLOAT_CLEAR_COLOR_VALUE_NV 0x888D +#define GL_FLOAT_RGBA_MODE_NV 0x888E +#endif + +#ifndef GL_NV_fragment_program +#define GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV 0x8868 +#define GL_FRAGMENT_PROGRAM_NV 0x8870 +#define GL_MAX_TEXTURE_COORDS_NV 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_NV 0x8872 +#define GL_FRAGMENT_PROGRAM_BINDING_NV 0x8873 +#define GL_PROGRAM_ERROR_STRING_NV 0x8874 +#endif + +#ifndef GL_NV_half_float +#define GL_HALF_FLOAT_NV 0x140B +#endif + +#ifndef GL_NV_pixel_data_range +#define GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878 +#define GL_READ_PIXEL_DATA_RANGE_NV 0x8879 +#define GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV 0x887A +#define GL_READ_PIXEL_DATA_RANGE_LENGTH_NV 0x887B +#define GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV 0x887C +#define GL_READ_PIXEL_DATA_RANGE_POINTER_NV 0x887D +#endif + +#ifndef GL_NV_primitive_restart +#define GL_PRIMITIVE_RESTART_NV 0x8558 +#define GL_PRIMITIVE_RESTART_INDEX_NV 0x8559 +#endif + +#ifndef GL_NV_texture_expand_normal +#define GL_TEXTURE_UNSIGNED_REMAP_MODE_NV 0x888F +#endif + +#ifndef GL_NV_vertex_program2 +#endif + +#ifndef GL_ATI_map_object_buffer +#endif + +#ifndef GL_ATI_separate_stencil +#define GL_STENCIL_BACK_FUNC_ATI 0x8800 +#define GL_STENCIL_BACK_FAIL_ATI 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI 0x8803 +#endif + +#ifndef GL_ATI_vertex_attrib_array_object +#endif + +#ifndef GL_OES_read_format +#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B +#endif + +#ifndef GL_EXT_depth_bounds_test +#define GL_DEPTH_BOUNDS_TEST_EXT 0x8890 +#define GL_DEPTH_BOUNDS_EXT 0x8891 +#endif + +#ifndef GL_EXT_texture_mirror_clamp +#define GL_MIRROR_CLAMP_EXT 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743 +#define GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912 +#endif + +#ifndef GL_EXT_blend_equation_separate +#define GL_BLEND_EQUATION_RGB_EXT GL_BLEND_EQUATION +#define GL_BLEND_EQUATION_ALPHA_EXT 0x883D +#endif + +#ifndef GL_MESA_pack_invert +#define GL_PACK_INVERT_MESA 0x8758 +#endif + +#ifndef GL_MESA_ycbcr_texture +#define GL_UNSIGNED_SHORT_8_8_MESA 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_MESA 0x85BB +#define GL_YCBCR_MESA 0x8757 +#endif + +#ifndef GL_EXT_pixel_buffer_object +#define GL_PIXEL_PACK_BUFFER_EXT 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_EXT 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_EXT 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_EXT 0x88EF +#endif + +#ifndef GL_NV_fragment_program_option +#endif + +#ifndef GL_NV_fragment_program2 +#define GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV 0x88F4 +#define GL_MAX_PROGRAM_CALL_DEPTH_NV 0x88F5 +#define GL_MAX_PROGRAM_IF_DEPTH_NV 0x88F6 +#define GL_MAX_PROGRAM_LOOP_DEPTH_NV 0x88F7 +#define GL_MAX_PROGRAM_LOOP_COUNT_NV 0x88F8 +#endif + +#ifndef GL_NV_vertex_program2_option +/* reuse GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV */ +/* reuse GL_MAX_PROGRAM_CALL_DEPTH_NV */ +#endif + +#ifndef GL_NV_vertex_program3 +/* reuse GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB */ +#endif + +#ifndef GL_EXT_framebuffer_object +#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 +#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 +#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT 0x8CD8 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 +#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 +#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 +#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 +#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 +#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA +#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB +#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC +#define GL_COLOR_ATTACHMENT13_EXT 0x8CED +#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE +#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF +#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 +#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 +#define GL_FRAMEBUFFER_EXT 0x8D40 +#define GL_RENDERBUFFER_EXT 0x8D41 +#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 +#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 +#define GL_STENCIL_INDEX1_EXT 0x8D46 +#define GL_STENCIL_INDEX4_EXT 0x8D47 +#define GL_STENCIL_INDEX8_EXT 0x8D48 +#define GL_STENCIL_INDEX16_EXT 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 +#endif + +#ifndef GL_GREMEDY_string_marker +#endif + + +/*************************************************************/ + +#include +#ifndef GL_VERSION_2_0 +/* GL type for program/shader text */ +typedef char GLchar; /* native character */ +#endif + +#ifndef GL_VERSION_1_5 +/* GL types for handling large vertex buffer objects */ +typedef ptrdiff_t GLintptr; +typedef ptrdiff_t GLsizeiptr; +#endif + +#ifndef GL_ARB_vertex_buffer_object +/* GL types for handling large vertex buffer objects */ +typedef ptrdiff_t GLintptrARB; +typedef ptrdiff_t GLsizeiptrARB; +#endif + +#ifndef GL_ARB_shader_objects +/* GL types for handling shader object handles and program/shader text */ +typedef char GLcharARB; /* native character */ +typedef unsigned int GLhandleARB; /* shader object handle */ +#endif + +/* GL types for "half" precision (s10e5) float data in host memory */ +#ifndef GL_ARB_half_float_pixel +typedef unsigned short GLhalfARB; +#endif + +#ifndef GL_NV_half_float +typedef unsigned short GLhalfNV; +#endif + +#ifndef GL_VERSION_1_2 +#define GL_VERSION_1_2 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendColor (GLclampf, GLclampf, GLclampf, GLclampf); +GLAPI void APIENTRY glBlendEquation (GLenum); +GLAPI void APIENTRY glDrawRangeElements (GLenum, GLuint, GLuint, GLsizei, GLenum, const GLvoid *); +GLAPI void APIENTRY glColorTable (GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *); +GLAPI void APIENTRY glColorTableParameterfv (GLenum, GLenum, const GLfloat *); +GLAPI void APIENTRY glColorTableParameteriv (GLenum, GLenum, const GLint *); +GLAPI void APIENTRY glCopyColorTable (GLenum, GLenum, GLint, GLint, GLsizei); +GLAPI void APIENTRY glGetColorTable (GLenum, GLenum, GLenum, GLvoid *); +GLAPI void APIENTRY glGetColorTableParameterfv (GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glGetColorTableParameteriv (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glColorSubTable (GLenum, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *); +GLAPI void APIENTRY glCopyColorSubTable (GLenum, GLsizei, GLint, GLint, GLsizei); +GLAPI void APIENTRY glConvolutionFilter1D (GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *); +GLAPI void APIENTRY glConvolutionFilter2D (GLenum, GLenum, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *); +GLAPI void APIENTRY glConvolutionParameterf (GLenum, GLenum, GLfloat); +GLAPI void APIENTRY glConvolutionParameterfv (GLenum, GLenum, const GLfloat *); +GLAPI void APIENTRY glConvolutionParameteri (GLenum, GLenum, GLint); +GLAPI void APIENTRY glConvolutionParameteriv (GLenum, GLenum, const GLint *); +GLAPI void APIENTRY glCopyConvolutionFilter1D (GLenum, GLenum, GLint, GLint, GLsizei); +GLAPI void APIENTRY glCopyConvolutionFilter2D (GLenum, GLenum, GLint, GLint, GLsizei, GLsizei); +GLAPI void APIENTRY glGetConvolutionFilter (GLenum, GLenum, GLenum, GLvoid *); +GLAPI void APIENTRY glGetConvolutionParameterfv (GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glGetConvolutionParameteriv (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetSeparableFilter (GLenum, GLenum, GLenum, GLvoid *, GLvoid *, GLvoid *); +GLAPI void APIENTRY glSeparableFilter2D (GLenum, GLenum, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *, const GLvoid *); +GLAPI void APIENTRY glGetHistogram (GLenum, GLboolean, GLenum, GLenum, GLvoid *); +GLAPI void APIENTRY glGetHistogramParameterfv (GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glGetHistogramParameteriv (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetMinmax (GLenum, GLboolean, GLenum, GLenum, GLvoid *); +GLAPI void APIENTRY glGetMinmaxParameterfv (GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glGetMinmaxParameteriv (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glHistogram (GLenum, GLsizei, GLenum, GLboolean); +GLAPI void APIENTRY glMinmax (GLenum, GLenum, GLboolean); +GLAPI void APIENTRY glResetHistogram (GLenum); +GLAPI void APIENTRY glResetMinmax (GLenum); +GLAPI void APIENTRY glTexImage3D (GLenum, GLint, GLint, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *); +GLAPI void APIENTRY glTexSubImage3D (GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *); +GLAPI void APIENTRY glCopyTexSubImage3D (GLenum, GLint, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); +typedef void (APIENTRYP PFNGLCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPROC) (GLenum target, GLenum format, GLenum type, GLvoid *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIPROC) (GLenum target, GLenum pname, GLint params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTERPROC) (GLenum target, GLenum format, GLenum type, GLvoid *image); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSEPARABLEFILTERPROC) (GLenum target, GLenum format, GLenum type, GLvoid *row, GLvoid *column, GLvoid *span); +typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMINMAXPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLHISTOGRAMPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLMINMAXPROC) (GLenum target, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLRESETHISTOGRAMPROC) (GLenum target); +typedef void (APIENTRYP PFNGLRESETMINMAXPROC) (GLenum target); +typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif + +#ifndef GL_VERSION_1_3 +#define GL_VERSION_1_3 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveTexture (GLenum); +GLAPI void APIENTRY glClientActiveTexture (GLenum); +GLAPI void APIENTRY glMultiTexCoord1d (GLenum, GLdouble); +GLAPI void APIENTRY glMultiTexCoord1dv (GLenum, const GLdouble *); +GLAPI void APIENTRY glMultiTexCoord1f (GLenum, GLfloat); +GLAPI void APIENTRY glMultiTexCoord1fv (GLenum, const GLfloat *); +GLAPI void APIENTRY glMultiTexCoord1i (GLenum, GLint); +GLAPI void APIENTRY glMultiTexCoord1iv (GLenum, const GLint *); +GLAPI void APIENTRY glMultiTexCoord1s (GLenum, GLshort); +GLAPI void APIENTRY glMultiTexCoord1sv (GLenum, const GLshort *); +GLAPI void APIENTRY glMultiTexCoord2d (GLenum, GLdouble, GLdouble); +GLAPI void APIENTRY glMultiTexCoord2dv (GLenum, const GLdouble *); +GLAPI void APIENTRY glMultiTexCoord2f (GLenum, GLfloat, GLfloat); +GLAPI void APIENTRY glMultiTexCoord2fv (GLenum, const GLfloat *); +GLAPI void APIENTRY glMultiTexCoord2i (GLenum, GLint, GLint); +GLAPI void APIENTRY glMultiTexCoord2iv (GLenum, const GLint *); +GLAPI void APIENTRY glMultiTexCoord2s (GLenum, GLshort, GLshort); +GLAPI void APIENTRY glMultiTexCoord2sv (GLenum, const GLshort *); +GLAPI void APIENTRY glMultiTexCoord3d (GLenum, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glMultiTexCoord3dv (GLenum, const GLdouble *); +GLAPI void APIENTRY glMultiTexCoord3f (GLenum, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glMultiTexCoord3fv (GLenum, const GLfloat *); +GLAPI void APIENTRY glMultiTexCoord3i (GLenum, GLint, GLint, GLint); +GLAPI void APIENTRY glMultiTexCoord3iv (GLenum, const GLint *); +GLAPI void APIENTRY glMultiTexCoord3s (GLenum, GLshort, GLshort, GLshort); +GLAPI void APIENTRY glMultiTexCoord3sv (GLenum, const GLshort *); +GLAPI void APIENTRY glMultiTexCoord4d (GLenum, GLdouble, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glMultiTexCoord4dv (GLenum, const GLdouble *); +GLAPI void APIENTRY glMultiTexCoord4f (GLenum, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glMultiTexCoord4fv (GLenum, const GLfloat *); +GLAPI void APIENTRY glMultiTexCoord4i (GLenum, GLint, GLint, GLint, GLint); +GLAPI void APIENTRY glMultiTexCoord4iv (GLenum, const GLint *); +GLAPI void APIENTRY glMultiTexCoord4s (GLenum, GLshort, GLshort, GLshort, GLshort); +GLAPI void APIENTRY glMultiTexCoord4sv (GLenum, const GLshort *); +GLAPI void APIENTRY glLoadTransposeMatrixf (const GLfloat *); +GLAPI void APIENTRY glLoadTransposeMatrixd (const GLdouble *); +GLAPI void APIENTRY glMultTransposeMatrixf (const GLfloat *); +GLAPI void APIENTRY glMultTransposeMatrixd (const GLdouble *); +GLAPI void APIENTRY glSampleCoverage (GLclampf, GLboolean); +GLAPI void APIENTRY glCompressedTexImage3D (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *); +GLAPI void APIENTRY glCompressedTexImage2D (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *); +GLAPI void APIENTRY glCompressedTexImage1D (GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const GLvoid *); +GLAPI void APIENTRY glCompressedTexSubImage3D (GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid *); +GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid *); +GLAPI void APIENTRY glCompressedTexSubImage1D (GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const GLvoid *); +GLAPI void APIENTRY glGetCompressedTexImage (GLenum, GLint, GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DPROC) (GLenum target, GLdouble s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC) (GLenum target, GLfloat s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IPROC) (GLenum target, GLint s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SPROC) (GLenum target, GLshort s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DPROC) (GLenum target, GLdouble s, GLdouble t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC) (GLenum target, GLfloat s, GLfloat t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IPROC) (GLenum target, GLint s, GLint t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SPROC) (GLenum target, GLshort s, GLshort t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IPROC) (GLenum target, GLint s, GLint t, GLint r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SPROC) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDPROC) (const GLdouble *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC) (const GLdouble *m); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, GLvoid *img); +#endif + +#ifndef GL_VERSION_1_4 +#define GL_VERSION_1_4 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparate (GLenum, GLenum, GLenum, GLenum); +GLAPI void APIENTRY glFogCoordf (GLfloat); +GLAPI void APIENTRY glFogCoordfv (const GLfloat *); +GLAPI void APIENTRY glFogCoordd (GLdouble); +GLAPI void APIENTRY glFogCoorddv (const GLdouble *); +GLAPI void APIENTRY glFogCoordPointer (GLenum, GLsizei, const GLvoid *); +GLAPI void APIENTRY glMultiDrawArrays (GLenum, GLint *, GLsizei *, GLsizei); +GLAPI void APIENTRY glMultiDrawElements (GLenum, const GLsizei *, GLenum, const GLvoid* *, GLsizei); +GLAPI void APIENTRY glPointParameterf (GLenum, GLfloat); +GLAPI void APIENTRY glPointParameterfv (GLenum, const GLfloat *); +GLAPI void APIENTRY glPointParameteri (GLenum, GLint); +GLAPI void APIENTRY glPointParameteriv (GLenum, const GLint *); +GLAPI void APIENTRY glSecondaryColor3b (GLbyte, GLbyte, GLbyte); +GLAPI void APIENTRY glSecondaryColor3bv (const GLbyte *); +GLAPI void APIENTRY glSecondaryColor3d (GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glSecondaryColor3dv (const GLdouble *); +GLAPI void APIENTRY glSecondaryColor3f (GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glSecondaryColor3fv (const GLfloat *); +GLAPI void APIENTRY glSecondaryColor3i (GLint, GLint, GLint); +GLAPI void APIENTRY glSecondaryColor3iv (const GLint *); +GLAPI void APIENTRY glSecondaryColor3s (GLshort, GLshort, GLshort); +GLAPI void APIENTRY glSecondaryColor3sv (const GLshort *); +GLAPI void APIENTRY glSecondaryColor3ub (GLubyte, GLubyte, GLubyte); +GLAPI void APIENTRY glSecondaryColor3ubv (const GLubyte *); +GLAPI void APIENTRY glSecondaryColor3ui (GLuint, GLuint, GLuint); +GLAPI void APIENTRY glSecondaryColor3uiv (const GLuint *); +GLAPI void APIENTRY glSecondaryColor3us (GLushort, GLushort, GLushort); +GLAPI void APIENTRY glSecondaryColor3usv (const GLushort *); +GLAPI void APIENTRY glSecondaryColorPointer (GLint, GLenum, GLsizei, const GLvoid *); +GLAPI void APIENTRY glWindowPos2d (GLdouble, GLdouble); +GLAPI void APIENTRY glWindowPos2dv (const GLdouble *); +GLAPI void APIENTRY glWindowPos2f (GLfloat, GLfloat); +GLAPI void APIENTRY glWindowPos2fv (const GLfloat *); +GLAPI void APIENTRY glWindowPos2i (GLint, GLint); +GLAPI void APIENTRY glWindowPos2iv (const GLint *); +GLAPI void APIENTRY glWindowPos2s (GLshort, GLshort); +GLAPI void APIENTRY glWindowPos2sv (const GLshort *); +GLAPI void APIENTRY glWindowPos3d (GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glWindowPos3dv (const GLdouble *); +GLAPI void APIENTRY glWindowPos3f (GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glWindowPos3fv (const GLfloat *); +GLAPI void APIENTRY glWindowPos3i (GLint, GLint, GLint); +GLAPI void APIENTRY glWindowPos3iv (const GLint *); +GLAPI void APIENTRY glWindowPos3s (GLshort, GLshort, GLshort); +GLAPI void APIENTRY glWindowPos3sv (const GLshort *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (APIENTRYP PFNGLFOGCOORDFPROC) (GLfloat coord); +typedef void (APIENTRYP PFNGLFOGCOORDFVPROC) (const GLfloat *coord); +typedef void (APIENTRYP PFNGLFOGCOORDDPROC) (GLdouble coord); +typedef void (APIENTRYP PFNGLFOGCOORDDVPROC) (const GLdouble *coord); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERPROC) (GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BPROC) (GLbyte red, GLbyte green, GLbyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DPROC) (GLdouble red, GLdouble green, GLdouble blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FPROC) (GLfloat red, GLfloat green, GLfloat blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IPROC) (GLint red, GLint green, GLint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SPROC) (GLshort red, GLshort green, GLshort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBPROC) (GLubyte red, GLubyte green, GLubyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVPROC) (const GLubyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIPROC) (GLuint red, GLuint green, GLuint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVPROC) (const GLuint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USPROC) (GLushort red, GLushort green, GLushort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVPROC) (const GLushort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLWINDOWPOS2DPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVPROC) (const GLshort *v); +#endif + +#ifndef GL_VERSION_1_5 +#define GL_VERSION_1_5 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenQueries (GLsizei, GLuint *); +GLAPI void APIENTRY glDeleteQueries (GLsizei, const GLuint *); +GLAPI GLboolean APIENTRY glIsQuery (GLuint); +GLAPI void APIENTRY glBeginQuery (GLenum, GLuint); +GLAPI void APIENTRY glEndQuery (GLenum); +GLAPI void APIENTRY glGetQueryiv (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetQueryObjectiv (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glGetQueryObjectuiv (GLuint, GLenum, GLuint *); +GLAPI void APIENTRY glBindBuffer (GLenum, GLuint); +GLAPI void APIENTRY glDeleteBuffers (GLsizei, const GLuint *); +GLAPI void APIENTRY glGenBuffers (GLsizei, GLuint *); +GLAPI GLboolean APIENTRY glIsBuffer (GLuint); +GLAPI void APIENTRY glBufferData (GLenum, GLsizeiptr, const GLvoid *, GLenum); +GLAPI void APIENTRY glBufferSubData (GLenum, GLintptr, GLsizeiptr, const GLvoid *); +GLAPI void APIENTRY glGetBufferSubData (GLenum, GLintptr, GLsizeiptr, GLvoid *); +GLAPI GLvoid* APIENTRY glMapBuffer (GLenum, GLenum); +GLAPI GLboolean APIENTRY glUnmapBuffer (GLenum); +GLAPI void APIENTRY glGetBufferParameteriv (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetBufferPointerv (GLenum, GLenum, GLvoid* *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data); +typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, GLvoid* *params); +#endif + +#ifndef GL_VERSION_2_0 +#define GL_VERSION_2_0 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparate (GLenum, GLenum); +GLAPI void APIENTRY glDrawBuffers (GLsizei, const GLenum *); +GLAPI void APIENTRY glStencilOpSeparate (GLenum, GLenum, GLenum, GLenum); +GLAPI void APIENTRY glStencilFuncSeparate (GLenum, GLenum, GLint, GLuint); +GLAPI void APIENTRY glStencilMaskSeparate (GLenum, GLuint); +GLAPI void APIENTRY glAttachShader (GLuint, GLuint); +GLAPI void APIENTRY glBindAttribLocation (GLuint, GLuint, const GLchar *); +GLAPI void APIENTRY glCompileShader (GLuint); +GLAPI GLuint APIENTRY glCreateProgram (void); +GLAPI GLuint APIENTRY glCreateShader (GLenum); +GLAPI void APIENTRY glDeleteProgram (GLuint); +GLAPI void APIENTRY glDeleteShader (GLuint); +GLAPI void APIENTRY glDetachShader (GLuint, GLuint); +GLAPI void APIENTRY glDisableVertexAttribArray (GLuint); +GLAPI void APIENTRY glEnableVertexAttribArray (GLuint); +GLAPI void APIENTRY glGetActiveAttrib (GLuint, GLuint, GLsizei, GLsizei *, GLint *, GLenum *, GLchar *); +GLAPI void APIENTRY glGetActiveUniform (GLuint, GLuint, GLsizei, GLsizei *, GLint *, GLenum *, GLchar *); +GLAPI void APIENTRY glGetAttachedShaders (GLuint, GLsizei, GLsizei *, GLuint *); +GLAPI GLint APIENTRY glGetAttribLocation (GLuint, const GLchar *); +GLAPI void APIENTRY glGetProgramiv (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glGetProgramInfoLog (GLuint, GLsizei, GLsizei *, GLchar *); +GLAPI void APIENTRY glGetShaderiv (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glGetShaderInfoLog (GLuint, GLsizei, GLsizei *, GLchar *); +GLAPI void APIENTRY glGetShaderSource (GLuint, GLsizei, GLsizei *, GLchar *); +GLAPI GLint APIENTRY glGetUniformLocation (GLuint, const GLchar *); +GLAPI void APIENTRY glGetUniformfv (GLuint, GLint, GLfloat *); +GLAPI void APIENTRY glGetUniformiv (GLuint, GLint, GLint *); +GLAPI void APIENTRY glGetVertexAttribdv (GLuint, GLenum, GLdouble *); +GLAPI void APIENTRY glGetVertexAttribfv (GLuint, GLenum, GLfloat *); +GLAPI void APIENTRY glGetVertexAttribiv (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint, GLenum, GLvoid* *); +GLAPI GLboolean APIENTRY glIsProgram (GLuint); +GLAPI GLboolean APIENTRY glIsShader (GLuint); +GLAPI void APIENTRY glLinkProgram (GLuint); +GLAPI void APIENTRY glShaderSource (GLuint, GLsizei, const GLchar* *, const GLint *); +GLAPI void APIENTRY glUseProgram (GLuint); +GLAPI void APIENTRY glUniform1f (GLint, GLfloat); +GLAPI void APIENTRY glUniform2f (GLint, GLfloat, GLfloat); +GLAPI void APIENTRY glUniform3f (GLint, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glUniform4f (GLint, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glUniform1i (GLint, GLint); +GLAPI void APIENTRY glUniform2i (GLint, GLint, GLint); +GLAPI void APIENTRY glUniform3i (GLint, GLint, GLint, GLint); +GLAPI void APIENTRY glUniform4i (GLint, GLint, GLint, GLint, GLint); +GLAPI void APIENTRY glUniform1fv (GLint, GLsizei, const GLfloat *); +GLAPI void APIENTRY glUniform2fv (GLint, GLsizei, const GLfloat *); +GLAPI void APIENTRY glUniform3fv (GLint, GLsizei, const GLfloat *); +GLAPI void APIENTRY glUniform4fv (GLint, GLsizei, const GLfloat *); +GLAPI void APIENTRY glUniform1iv (GLint, GLsizei, const GLint *); +GLAPI void APIENTRY glUniform2iv (GLint, GLsizei, const GLint *); +GLAPI void APIENTRY glUniform3iv (GLint, GLsizei, const GLint *); +GLAPI void APIENTRY glUniform4iv (GLint, GLsizei, const GLint *); +GLAPI void APIENTRY glUniformMatrix2fv (GLint, GLsizei, GLboolean, const GLfloat *); +GLAPI void APIENTRY glUniformMatrix3fv (GLint, GLsizei, GLboolean, const GLfloat *); +GLAPI void APIENTRY glUniformMatrix4fv (GLint, GLsizei, GLboolean, const GLfloat *); +GLAPI void APIENTRY glValidateProgram (GLuint); +GLAPI void APIENTRY glVertexAttrib1d (GLuint, GLdouble); +GLAPI void APIENTRY glVertexAttrib1dv (GLuint, const GLdouble *); +GLAPI void APIENTRY glVertexAttrib1f (GLuint, GLfloat); +GLAPI void APIENTRY glVertexAttrib1fv (GLuint, const GLfloat *); +GLAPI void APIENTRY glVertexAttrib1s (GLuint, GLshort); +GLAPI void APIENTRY glVertexAttrib1sv (GLuint, const GLshort *); +GLAPI void APIENTRY glVertexAttrib2d (GLuint, GLdouble, GLdouble); +GLAPI void APIENTRY glVertexAttrib2dv (GLuint, const GLdouble *); +GLAPI void APIENTRY glVertexAttrib2f (GLuint, GLfloat, GLfloat); +GLAPI void APIENTRY glVertexAttrib2fv (GLuint, const GLfloat *); +GLAPI void APIENTRY glVertexAttrib2s (GLuint, GLshort, GLshort); +GLAPI void APIENTRY glVertexAttrib2sv (GLuint, const GLshort *); +GLAPI void APIENTRY glVertexAttrib3d (GLuint, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glVertexAttrib3dv (GLuint, const GLdouble *); +GLAPI void APIENTRY glVertexAttrib3f (GLuint, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glVertexAttrib3fv (GLuint, const GLfloat *); +GLAPI void APIENTRY glVertexAttrib3s (GLuint, GLshort, GLshort, GLshort); +GLAPI void APIENTRY glVertexAttrib3sv (GLuint, const GLshort *); +GLAPI void APIENTRY glVertexAttrib4Nbv (GLuint, const GLbyte *); +GLAPI void APIENTRY glVertexAttrib4Niv (GLuint, const GLint *); +GLAPI void APIENTRY glVertexAttrib4Nsv (GLuint, const GLshort *); +GLAPI void APIENTRY glVertexAttrib4Nub (GLuint, GLubyte, GLubyte, GLubyte, GLubyte); +GLAPI void APIENTRY glVertexAttrib4Nubv (GLuint, const GLubyte *); +GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint, const GLuint *); +GLAPI void APIENTRY glVertexAttrib4Nusv (GLuint, const GLushort *); +GLAPI void APIENTRY glVertexAttrib4bv (GLuint, const GLbyte *); +GLAPI void APIENTRY glVertexAttrib4d (GLuint, GLdouble, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glVertexAttrib4dv (GLuint, const GLdouble *); +GLAPI void APIENTRY glVertexAttrib4f (GLuint, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glVertexAttrib4fv (GLuint, const GLfloat *); +GLAPI void APIENTRY glVertexAttrib4iv (GLuint, const GLint *); +GLAPI void APIENTRY glVertexAttrib4s (GLuint, GLshort, GLshort, GLshort, GLshort); +GLAPI void APIENTRY glVertexAttrib4sv (GLuint, const GLshort *); +GLAPI void APIENTRY glVertexAttrib4ubv (GLuint, const GLubyte *); +GLAPI void APIENTRY glVertexAttrib4uiv (GLuint, const GLuint *); +GLAPI void APIENTRY glVertexAttrib4usv (GLuint, const GLushort *); +GLAPI void APIENTRY glVertexAttribPointer (GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask); +typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); +typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *obj); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, GLvoid* *pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); +typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar* *string, const GLint *length); +typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer); +#endif + +#ifndef GL_ARB_multitexture +#define GL_ARB_multitexture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveTextureARB (GLenum); +GLAPI void APIENTRY glClientActiveTextureARB (GLenum); +GLAPI void APIENTRY glMultiTexCoord1dARB (GLenum, GLdouble); +GLAPI void APIENTRY glMultiTexCoord1dvARB (GLenum, const GLdouble *); +GLAPI void APIENTRY glMultiTexCoord1fARB (GLenum, GLfloat); +GLAPI void APIENTRY glMultiTexCoord1fvARB (GLenum, const GLfloat *); +GLAPI void APIENTRY glMultiTexCoord1iARB (GLenum, GLint); +GLAPI void APIENTRY glMultiTexCoord1ivARB (GLenum, const GLint *); +GLAPI void APIENTRY glMultiTexCoord1sARB (GLenum, GLshort); +GLAPI void APIENTRY glMultiTexCoord1svARB (GLenum, const GLshort *); +GLAPI void APIENTRY glMultiTexCoord2dARB (GLenum, GLdouble, GLdouble); +GLAPI void APIENTRY glMultiTexCoord2dvARB (GLenum, const GLdouble *); +GLAPI void APIENTRY glMultiTexCoord2fARB (GLenum, GLfloat, GLfloat); +GLAPI void APIENTRY glMultiTexCoord2fvARB (GLenum, const GLfloat *); +GLAPI void APIENTRY glMultiTexCoord2iARB (GLenum, GLint, GLint); +GLAPI void APIENTRY glMultiTexCoord2ivARB (GLenum, const GLint *); +GLAPI void APIENTRY glMultiTexCoord2sARB (GLenum, GLshort, GLshort); +GLAPI void APIENTRY glMultiTexCoord2svARB (GLenum, const GLshort *); +GLAPI void APIENTRY glMultiTexCoord3dARB (GLenum, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glMultiTexCoord3dvARB (GLenum, const GLdouble *); +GLAPI void APIENTRY glMultiTexCoord3fARB (GLenum, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glMultiTexCoord3fvARB (GLenum, const GLfloat *); +GLAPI void APIENTRY glMultiTexCoord3iARB (GLenum, GLint, GLint, GLint); +GLAPI void APIENTRY glMultiTexCoord3ivARB (GLenum, const GLint *); +GLAPI void APIENTRY glMultiTexCoord3sARB (GLenum, GLshort, GLshort, GLshort); +GLAPI void APIENTRY glMultiTexCoord3svARB (GLenum, const GLshort *); +GLAPI void APIENTRY glMultiTexCoord4dARB (GLenum, GLdouble, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glMultiTexCoord4dvARB (GLenum, const GLdouble *); +GLAPI void APIENTRY glMultiTexCoord4fARB (GLenum, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glMultiTexCoord4fvARB (GLenum, const GLfloat *); +GLAPI void APIENTRY glMultiTexCoord4iARB (GLenum, GLint, GLint, GLint, GLint); +GLAPI void APIENTRY glMultiTexCoord4ivARB (GLenum, const GLint *); +GLAPI void APIENTRY glMultiTexCoord4sARB (GLenum, GLshort, GLshort, GLshort, GLshort); +GLAPI void APIENTRY glMultiTexCoord4svARB (GLenum, const GLshort *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DARBPROC) (GLenum target, GLdouble s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FARBPROC) (GLenum target, GLfloat s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IARBPROC) (GLenum target, GLint s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SARBPROC) (GLenum target, GLshort s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DARBPROC) (GLenum target, GLdouble s, GLdouble t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IARBPROC) (GLenum target, GLint s, GLint t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SARBPROC) (GLenum target, GLshort s, GLshort t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IARBPROC) (GLenum target, GLint s, GLint t, GLint r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IARBPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVARBPROC) (GLenum target, const GLshort *v); +#endif + +#ifndef GL_ARB_transpose_matrix +#define GL_ARB_transpose_matrix 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLoadTransposeMatrixfARB (const GLfloat *); +GLAPI void APIENTRY glLoadTransposeMatrixdARB (const GLdouble *); +GLAPI void APIENTRY glMultTransposeMatrixfARB (const GLfloat *); +GLAPI void APIENTRY glMultTransposeMatrixdARB (const GLdouble *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); +#endif + +#ifndef GL_ARB_multisample +#define GL_ARB_multisample 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleCoverageARB (GLclampf, GLboolean); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEARBPROC) (GLclampf value, GLboolean invert); +#endif + +#ifndef GL_ARB_texture_env_add +#define GL_ARB_texture_env_add 1 +#endif + +#ifndef GL_ARB_texture_cube_map +#define GL_ARB_texture_cube_map 1 +#endif + +#ifndef GL_ARB_texture_compression +#define GL_ARB_texture_compression 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCompressedTexImage3DARB (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *); +GLAPI void APIENTRY glCompressedTexImage2DARB (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *); +GLAPI void APIENTRY glCompressedTexImage1DARB (GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const GLvoid *); +GLAPI void APIENTRY glCompressedTexSubImage3DARB (GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid *); +GLAPI void APIENTRY glCompressedTexSubImage2DARB (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid *); +GLAPI void APIENTRY glCompressedTexSubImage1DARB (GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const GLvoid *); +GLAPI void APIENTRY glGetCompressedTexImageARB (GLenum, GLint, GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint level, GLvoid *img); +#endif + +#ifndef GL_ARB_texture_border_clamp +#define GL_ARB_texture_border_clamp 1 +#endif + +#ifndef GL_ARB_point_parameters +#define GL_ARB_point_parameters 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfARB (GLenum, GLfloat); +GLAPI void APIENTRY glPointParameterfvARB (GLenum, const GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPOINTPARAMETERFARBPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVARBPROC) (GLenum pname, const GLfloat *params); +#endif + +#ifndef GL_ARB_vertex_blend +#define GL_ARB_vertex_blend 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWeightbvARB (GLint, const GLbyte *); +GLAPI void APIENTRY glWeightsvARB (GLint, const GLshort *); +GLAPI void APIENTRY glWeightivARB (GLint, const GLint *); +GLAPI void APIENTRY glWeightfvARB (GLint, const GLfloat *); +GLAPI void APIENTRY glWeightdvARB (GLint, const GLdouble *); +GLAPI void APIENTRY glWeightubvARB (GLint, const GLubyte *); +GLAPI void APIENTRY glWeightusvARB (GLint, const GLushort *); +GLAPI void APIENTRY glWeightuivARB (GLint, const GLuint *); +GLAPI void APIENTRY glWeightPointerARB (GLint, GLenum, GLsizei, const GLvoid *); +GLAPI void APIENTRY glVertexBlendARB (GLint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLWEIGHTBVARBPROC) (GLint size, const GLbyte *weights); +typedef void (APIENTRYP PFNGLWEIGHTSVARBPROC) (GLint size, const GLshort *weights); +typedef void (APIENTRYP PFNGLWEIGHTIVARBPROC) (GLint size, const GLint *weights); +typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC) (GLint size, const GLfloat *weights); +typedef void (APIENTRYP PFNGLWEIGHTDVARBPROC) (GLint size, const GLdouble *weights); +typedef void (APIENTRYP PFNGLWEIGHTUBVARBPROC) (GLint size, const GLubyte *weights); +typedef void (APIENTRYP PFNGLWEIGHTUSVARBPROC) (GLint size, const GLushort *weights); +typedef void (APIENTRYP PFNGLWEIGHTUIVARBPROC) (GLint size, const GLuint *weights); +typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC) (GLint count); +#endif + +#ifndef GL_ARB_matrix_palette +#define GL_ARB_matrix_palette 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCurrentPaletteMatrixARB (GLint); +GLAPI void APIENTRY glMatrixIndexubvARB (GLint, const GLubyte *); +GLAPI void APIENTRY glMatrixIndexusvARB (GLint, const GLushort *); +GLAPI void APIENTRY glMatrixIndexuivARB (GLint, const GLuint *); +GLAPI void APIENTRY glMatrixIndexPointerARB (GLint, GLenum, GLsizei, const GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCURRENTPALETTEMATRIXARBPROC) (GLint index); +typedef void (APIENTRYP PFNGLMATRIXINDEXUBVARBPROC) (GLint size, const GLubyte *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXUSVARBPROC) (GLint size, const GLushort *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXUIVARBPROC) (GLint size, const GLuint *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +#ifndef GL_ARB_texture_env_combine +#define GL_ARB_texture_env_combine 1 +#endif + +#ifndef GL_ARB_texture_env_crossbar +#define GL_ARB_texture_env_crossbar 1 +#endif + +#ifndef GL_ARB_texture_env_dot3 +#define GL_ARB_texture_env_dot3 1 +#endif + +#ifndef GL_ARB_texture_mirrored_repeat +#define GL_ARB_texture_mirrored_repeat 1 +#endif + +#ifndef GL_ARB_depth_texture +#define GL_ARB_depth_texture 1 +#endif + +#ifndef GL_ARB_shadow +#define GL_ARB_shadow 1 +#endif + +#ifndef GL_ARB_shadow_ambient +#define GL_ARB_shadow_ambient 1 +#endif + +#ifndef GL_ARB_window_pos +#define GL_ARB_window_pos 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWindowPos2dARB (GLdouble, GLdouble); +GLAPI void APIENTRY glWindowPos2dvARB (const GLdouble *); +GLAPI void APIENTRY glWindowPos2fARB (GLfloat, GLfloat); +GLAPI void APIENTRY glWindowPos2fvARB (const GLfloat *); +GLAPI void APIENTRY glWindowPos2iARB (GLint, GLint); +GLAPI void APIENTRY glWindowPos2ivARB (const GLint *); +GLAPI void APIENTRY glWindowPos2sARB (GLshort, GLshort); +GLAPI void APIENTRY glWindowPos2svARB (const GLshort *); +GLAPI void APIENTRY glWindowPos3dARB (GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glWindowPos3dvARB (const GLdouble *); +GLAPI void APIENTRY glWindowPos3fARB (GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glWindowPos3fvARB (const GLfloat *); +GLAPI void APIENTRY glWindowPos3iARB (GLint, GLint, GLint); +GLAPI void APIENTRY glWindowPos3ivARB (const GLint *); +GLAPI void APIENTRY glWindowPos3sARB (GLshort, GLshort, GLshort); +GLAPI void APIENTRY glWindowPos3svARB (const GLshort *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLWINDOWPOS2DARBPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVARBPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FARBPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVARBPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IARBPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVARBPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SARBPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVARBPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DARBPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVARBPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FARBPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVARBPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IARBPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVARBPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SARBPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVARBPROC) (const GLshort *v); +#endif + +#ifndef GL_ARB_vertex_program +#define GL_ARB_vertex_program 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttrib1dARB (GLuint, GLdouble); +GLAPI void APIENTRY glVertexAttrib1dvARB (GLuint, const GLdouble *); +GLAPI void APIENTRY glVertexAttrib1fARB (GLuint, GLfloat); +GLAPI void APIENTRY glVertexAttrib1fvARB (GLuint, const GLfloat *); +GLAPI void APIENTRY glVertexAttrib1sARB (GLuint, GLshort); +GLAPI void APIENTRY glVertexAttrib1svARB (GLuint, const GLshort *); +GLAPI void APIENTRY glVertexAttrib2dARB (GLuint, GLdouble, GLdouble); +GLAPI void APIENTRY glVertexAttrib2dvARB (GLuint, const GLdouble *); +GLAPI void APIENTRY glVertexAttrib2fARB (GLuint, GLfloat, GLfloat); +GLAPI void APIENTRY glVertexAttrib2fvARB (GLuint, const GLfloat *); +GLAPI void APIENTRY glVertexAttrib2sARB (GLuint, GLshort, GLshort); +GLAPI void APIENTRY glVertexAttrib2svARB (GLuint, const GLshort *); +GLAPI void APIENTRY glVertexAttrib3dARB (GLuint, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glVertexAttrib3dvARB (GLuint, const GLdouble *); +GLAPI void APIENTRY glVertexAttrib3fARB (GLuint, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glVertexAttrib3fvARB (GLuint, const GLfloat *); +GLAPI void APIENTRY glVertexAttrib3sARB (GLuint, GLshort, GLshort, GLshort); +GLAPI void APIENTRY glVertexAttrib3svARB (GLuint, const GLshort *); +GLAPI void APIENTRY glVertexAttrib4NbvARB (GLuint, const GLbyte *); +GLAPI void APIENTRY glVertexAttrib4NivARB (GLuint, const GLint *); +GLAPI void APIENTRY glVertexAttrib4NsvARB (GLuint, const GLshort *); +GLAPI void APIENTRY glVertexAttrib4NubARB (GLuint, GLubyte, GLubyte, GLubyte, GLubyte); +GLAPI void APIENTRY glVertexAttrib4NubvARB (GLuint, const GLubyte *); +GLAPI void APIENTRY glVertexAttrib4NuivARB (GLuint, const GLuint *); +GLAPI void APIENTRY glVertexAttrib4NusvARB (GLuint, const GLushort *); +GLAPI void APIENTRY glVertexAttrib4bvARB (GLuint, const GLbyte *); +GLAPI void APIENTRY glVertexAttrib4dARB (GLuint, GLdouble, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glVertexAttrib4dvARB (GLuint, const GLdouble *); +GLAPI void APIENTRY glVertexAttrib4fARB (GLuint, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glVertexAttrib4fvARB (GLuint, const GLfloat *); +GLAPI void APIENTRY glVertexAttrib4ivARB (GLuint, const GLint *); +GLAPI void APIENTRY glVertexAttrib4sARB (GLuint, GLshort, GLshort, GLshort, GLshort); +GLAPI void APIENTRY glVertexAttrib4svARB (GLuint, const GLshort *); +GLAPI void APIENTRY glVertexAttrib4ubvARB (GLuint, const GLubyte *); +GLAPI void APIENTRY glVertexAttrib4uivARB (GLuint, const GLuint *); +GLAPI void APIENTRY glVertexAttrib4usvARB (GLuint, const GLushort *); +GLAPI void APIENTRY glVertexAttribPointerARB (GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid *); +GLAPI void APIENTRY glEnableVertexAttribArrayARB (GLuint); +GLAPI void APIENTRY glDisableVertexAttribArrayARB (GLuint); +GLAPI void APIENTRY glProgramStringARB (GLenum, GLenum, GLsizei, const GLvoid *); +GLAPI void APIENTRY glBindProgramARB (GLenum, GLuint); +GLAPI void APIENTRY glDeleteProgramsARB (GLsizei, const GLuint *); +GLAPI void APIENTRY glGenProgramsARB (GLsizei, GLuint *); +GLAPI void APIENTRY glProgramEnvParameter4dARB (GLenum, GLuint, GLdouble, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glProgramEnvParameter4dvARB (GLenum, GLuint, const GLdouble *); +GLAPI void APIENTRY glProgramEnvParameter4fARB (GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glProgramEnvParameter4fvARB (GLenum, GLuint, const GLfloat *); +GLAPI void APIENTRY glProgramLocalParameter4dARB (GLenum, GLuint, GLdouble, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glProgramLocalParameter4dvARB (GLenum, GLuint, const GLdouble *); +GLAPI void APIENTRY glProgramLocalParameter4fARB (GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glProgramLocalParameter4fvARB (GLenum, GLuint, const GLfloat *); +GLAPI void APIENTRY glGetProgramEnvParameterdvARB (GLenum, GLuint, GLdouble *); +GLAPI void APIENTRY glGetProgramEnvParameterfvARB (GLenum, GLuint, GLfloat *); +GLAPI void APIENTRY glGetProgramLocalParameterdvARB (GLenum, GLuint, GLdouble *); +GLAPI void APIENTRY glGetProgramLocalParameterfvARB (GLenum, GLuint, GLfloat *); +GLAPI void APIENTRY glGetProgramivARB (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetProgramStringARB (GLenum, GLenum, GLvoid *); +GLAPI void APIENTRY glGetVertexAttribdvARB (GLuint, GLenum, GLdouble *); +GLAPI void APIENTRY glGetVertexAttribfvARB (GLuint, GLenum, GLfloat *); +GLAPI void APIENTRY glGetVertexAttribivARB (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glGetVertexAttribPointervARB (GLuint, GLenum, GLvoid* *); +GLAPI GLboolean APIENTRY glIsProgramARB (GLuint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DARBPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FARBPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SARBPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DARBPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FARBPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SARBPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVARBPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVARBPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBARBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVARBPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVARBPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVARBPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVARBPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVARBPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVARBPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVARBPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVARBPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERARBPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); +typedef void (APIENTRYP PFNGLPROGRAMSTRINGARBPROC) (GLenum target, GLenum format, GLsizei len, const GLvoid *string); +typedef void (APIENTRYP PFNGLBINDPROGRAMARBPROC) (GLenum target, GLuint program); +typedef void (APIENTRYP PFNGLDELETEPROGRAMSARBPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLGENPROGRAMSARBPROC) (GLsizei n, GLuint *programs); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGARBPROC) (GLenum target, GLenum pname, GLvoid *string); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVARBPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVARBPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVARBPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVARBPROC) (GLuint index, GLenum pname, GLvoid* *pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMARBPROC) (GLuint program); +#endif + +#ifndef GL_ARB_fragment_program +#define GL_ARB_fragment_program 1 +/* All ARB_fragment_program entry points are shared with ARB_vertex_program. */ +#endif + +#ifndef GL_ARB_vertex_buffer_object +#define GL_ARB_vertex_buffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindBufferARB (GLenum, GLuint); +GLAPI void APIENTRY glDeleteBuffersARB (GLsizei, const GLuint *); +GLAPI void APIENTRY glGenBuffersARB (GLsizei, GLuint *); +GLAPI GLboolean APIENTRY glIsBufferARB (GLuint); +GLAPI void APIENTRY glBufferDataARB (GLenum, GLsizeiptrARB, const GLvoid *, GLenum); +GLAPI void APIENTRY glBufferSubDataARB (GLenum, GLintptrARB, GLsizeiptrARB, const GLvoid *); +GLAPI void APIENTRY glGetBufferSubDataARB (GLenum, GLintptrARB, GLsizeiptrARB, GLvoid *); +GLAPI GLvoid* APIENTRY glMapBufferARB (GLenum, GLenum); +GLAPI GLboolean APIENTRY glUnmapBufferARB (GLenum); +GLAPI void APIENTRY glGetBufferParameterivARB (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetBufferPointervARB (GLenum, GLenum, GLvoid* *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERARBPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAARBPROC) (GLenum target, GLsizeiptrARB size, const GLvoid *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid *data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid *data); +typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVARBPROC) (GLenum target, GLenum pname, GLvoid* *params); +#endif + +#ifndef GL_ARB_occlusion_query +#define GL_ARB_occlusion_query 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenQueriesARB (GLsizei, GLuint *); +GLAPI void APIENTRY glDeleteQueriesARB (GLsizei, const GLuint *); +GLAPI GLboolean APIENTRY glIsQueryARB (GLuint); +GLAPI void APIENTRY glBeginQueryARB (GLenum, GLuint); +GLAPI void APIENTRY glEndQueryARB (GLenum); +GLAPI void APIENTRY glGetQueryivARB (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetQueryObjectivARB (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glGetQueryObjectuivARB (GLuint, GLenum, GLuint *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGENQUERIESARBPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESARBPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYARBPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYARBPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYARBPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVARBPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVARBPROC) (GLuint id, GLenum pname, GLuint *params); +#endif + +#ifndef GL_ARB_shader_objects +#define GL_ARB_shader_objects 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeleteObjectARB (GLhandleARB); +GLAPI GLhandleARB APIENTRY glGetHandleARB (GLenum); +GLAPI void APIENTRY glDetachObjectARB (GLhandleARB, GLhandleARB); +GLAPI GLhandleARB APIENTRY glCreateShaderObjectARB (GLenum); +GLAPI void APIENTRY glShaderSourceARB (GLhandleARB, GLsizei, const GLcharARB* *, const GLint *); +GLAPI void APIENTRY glCompileShaderARB (GLhandleARB); +GLAPI GLhandleARB APIENTRY glCreateProgramObjectARB (void); +GLAPI void APIENTRY glAttachObjectARB (GLhandleARB, GLhandleARB); +GLAPI void APIENTRY glLinkProgramARB (GLhandleARB); +GLAPI void APIENTRY glUseProgramObjectARB (GLhandleARB); +GLAPI void APIENTRY glValidateProgramARB (GLhandleARB); +GLAPI void APIENTRY glUniform1fARB (GLint, GLfloat); +GLAPI void APIENTRY glUniform2fARB (GLint, GLfloat, GLfloat); +GLAPI void APIENTRY glUniform3fARB (GLint, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glUniform4fARB (GLint, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glUniform1iARB (GLint, GLint); +GLAPI void APIENTRY glUniform2iARB (GLint, GLint, GLint); +GLAPI void APIENTRY glUniform3iARB (GLint, GLint, GLint, GLint); +GLAPI void APIENTRY glUniform4iARB (GLint, GLint, GLint, GLint, GLint); +GLAPI void APIENTRY glUniform1fvARB (GLint, GLsizei, const GLfloat *); +GLAPI void APIENTRY glUniform2fvARB (GLint, GLsizei, const GLfloat *); +GLAPI void APIENTRY glUniform3fvARB (GLint, GLsizei, const GLfloat *); +GLAPI void APIENTRY glUniform4fvARB (GLint, GLsizei, const GLfloat *); +GLAPI void APIENTRY glUniform1ivARB (GLint, GLsizei, const GLint *); +GLAPI void APIENTRY glUniform2ivARB (GLint, GLsizei, const GLint *); +GLAPI void APIENTRY glUniform3ivARB (GLint, GLsizei, const GLint *); +GLAPI void APIENTRY glUniform4ivARB (GLint, GLsizei, const GLint *); +GLAPI void APIENTRY glUniformMatrix2fvARB (GLint, GLsizei, GLboolean, const GLfloat *); +GLAPI void APIENTRY glUniformMatrix3fvARB (GLint, GLsizei, GLboolean, const GLfloat *); +GLAPI void APIENTRY glUniformMatrix4fvARB (GLint, GLsizei, GLboolean, const GLfloat *); +GLAPI void APIENTRY glGetObjectParameterfvARB (GLhandleARB, GLenum, GLfloat *); +GLAPI void APIENTRY glGetObjectParameterivARB (GLhandleARB, GLenum, GLint *); +GLAPI void APIENTRY glGetInfoLogARB (GLhandleARB, GLsizei, GLsizei *, GLcharARB *); +GLAPI void APIENTRY glGetAttachedObjectsARB (GLhandleARB, GLsizei, GLsizei *, GLhandleARB *); +GLAPI GLint APIENTRY glGetUniformLocationARB (GLhandleARB, const GLcharARB *); +GLAPI void APIENTRY glGetActiveUniformARB (GLhandleARB, GLuint, GLsizei, GLsizei *, GLint *, GLenum *, GLcharARB *); +GLAPI void APIENTRY glGetUniformfvARB (GLhandleARB, GLint, GLfloat *); +GLAPI void APIENTRY glGetUniformivARB (GLhandleARB, GLint, GLint *); +GLAPI void APIENTRY glGetShaderSourceARB (GLhandleARB, GLsizei, GLsizei *, GLcharARB *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDELETEOBJECTARBPROC) (GLhandleARB obj); +typedef GLhandleARB (APIENTRYP PFNGLGETHANDLEARBPROC) (GLenum pname); +typedef void (APIENTRYP PFNGLDETACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB attachedObj); +typedef GLhandleARB (APIENTRYP PFNGLCREATESHADEROBJECTARBPROC) (GLenum shaderType); +typedef void (APIENTRYP PFNGLSHADERSOURCEARBPROC) (GLhandleARB shaderObj, GLsizei count, const GLcharARB* *string, const GLint *length); +typedef void (APIENTRYP PFNGLCOMPILESHADERARBPROC) (GLhandleARB shaderObj); +typedef GLhandleARB (APIENTRYP PFNGLCREATEPROGRAMOBJECTARBPROC) (void); +typedef void (APIENTRYP PFNGLATTACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB obj); +typedef void (APIENTRYP PFNGLLINKPROGRAMARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLUSEPROGRAMOBJECTARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLUNIFORM1FARBPROC) (GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FARBPROC) (GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IARBPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IARBPROC) (GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM2FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM3FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM4FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM1IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM2IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM3IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM4IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERFVARBPROC) (GLhandleARB obj, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVARBPROC) (GLhandleARB obj, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETINFOLOGARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); +typedef void (APIENTRYP PFNGLGETATTACHEDOBJECTSARBPROC) (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVARBPROC) (GLhandleARB programObj, GLint location, GLfloat *params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVARBPROC) (GLhandleARB programObj, GLint location, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); +#endif + +#ifndef GL_ARB_vertex_shader +#define GL_ARB_vertex_shader 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindAttribLocationARB (GLhandleARB, GLuint, const GLcharARB *); +GLAPI void APIENTRY glGetActiveAttribARB (GLhandleARB, GLuint, GLsizei, GLsizei *, GLint *, GLenum *, GLcharARB *); +GLAPI GLint APIENTRY glGetAttribLocationARB (GLhandleARB, const GLcharARB *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONARBPROC) (GLhandleARB programObj, GLuint index, const GLcharARB *name); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); +#endif + +#ifndef GL_ARB_fragment_shader +#define GL_ARB_fragment_shader 1 +#endif + +#ifndef GL_ARB_shading_language_100 +#define GL_ARB_shading_language_100 1 +#endif + +#ifndef GL_ARB_texture_non_power_of_two +#define GL_ARB_texture_non_power_of_two 1 +#endif + +#ifndef GL_ARB_point_sprite +#define GL_ARB_point_sprite 1 +#endif + +#ifndef GL_ARB_fragment_program_shadow +#define GL_ARB_fragment_program_shadow 1 +#endif + +#ifndef GL_ARB_draw_buffers +#define GL_ARB_draw_buffers 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawBuffersARB (GLsizei, const GLenum *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWBUFFERSARBPROC) (GLsizei n, const GLenum *bufs); +#endif + +#ifndef GL_ARB_texture_rectangle +#define GL_ARB_texture_rectangle 1 +#endif + +#ifndef GL_ARB_color_buffer_float +#define GL_ARB_color_buffer_float 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClampColorARB (GLenum, GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCLAMPCOLORARBPROC) (GLenum target, GLenum clamp); +#endif + +#ifndef GL_ARB_half_float_pixel +#define GL_ARB_half_float_pixel 1 +#endif + +#ifndef GL_ARB_texture_float +#define GL_ARB_texture_float 1 +#endif + +#ifndef GL_ARB_pixel_buffer_object +#define GL_ARB_pixel_buffer_object 1 +#endif + +#ifndef GL_EXT_abgr +#define GL_EXT_abgr 1 +#endif + +#ifndef GL_EXT_blend_color +#define GL_EXT_blend_color 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendColorEXT (GLclampf, GLclampf, GLclampf, GLclampf); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDCOLOREXTPROC) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +#endif + +#ifndef GL_EXT_polygon_offset +#define GL_EXT_polygon_offset 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPolygonOffsetEXT (GLfloat, GLfloat); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPOLYGONOFFSETEXTPROC) (GLfloat factor, GLfloat bias); +#endif + +#ifndef GL_EXT_texture +#define GL_EXT_texture 1 +#endif + +#ifndef GL_EXT_texture3D +#define GL_EXT_texture3D 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage3DEXT (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *); +GLAPI void APIENTRY glTexSubImage3DEXT (GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXIMAGE3DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +#endif + +#ifndef GL_SGIS_texture_filter4 +#define GL_SGIS_texture_filter4 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetTexFilterFuncSGIS (GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glTexFilterFuncSGIS (GLenum, GLenum, GLsizei, const GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLfloat *weights); +typedef void (APIENTRYP PFNGLTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); +#endif + +#ifndef GL_EXT_subtexture +#define GL_EXT_subtexture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexSubImage1DEXT (GLenum, GLint, GLint, GLsizei, GLenum, GLenum, const GLvoid *); +GLAPI void APIENTRY glTexSubImage2DEXT (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +#endif + +#ifndef GL_EXT_copy_texture +#define GL_EXT_copy_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCopyTexImage1DEXT (GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLint); +GLAPI void APIENTRY glCopyTexImage2DEXT (GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint); +GLAPI void APIENTRY glCopyTexSubImage1DEXT (GLenum, GLint, GLint, GLint, GLint, GLsizei); +GLAPI void APIENTRY glCopyTexSubImage2DEXT (GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei); +GLAPI void APIENTRY glCopyTexSubImage3DEXT (GLenum, GLint, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif + +#ifndef GL_EXT_histogram +#define GL_EXT_histogram 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetHistogramEXT (GLenum, GLboolean, GLenum, GLenum, GLvoid *); +GLAPI void APIENTRY glGetHistogramParameterfvEXT (GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glGetHistogramParameterivEXT (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetMinmaxEXT (GLenum, GLboolean, GLenum, GLenum, GLvoid *); +GLAPI void APIENTRY glGetMinmaxParameterfvEXT (GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glGetMinmaxParameterivEXT (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glHistogramEXT (GLenum, GLsizei, GLenum, GLboolean); +GLAPI void APIENTRY glMinmaxEXT (GLenum, GLenum, GLboolean); +GLAPI void APIENTRY glResetHistogramEXT (GLenum); +GLAPI void APIENTRY glResetMinmaxEXT (GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETHISTOGRAMEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMINMAXEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLHISTOGRAMEXTPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLMINMAXEXTPROC) (GLenum target, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLRESETHISTOGRAMEXTPROC) (GLenum target); +typedef void (APIENTRYP PFNGLRESETMINMAXEXTPROC) (GLenum target); +#endif + +#ifndef GL_EXT_convolution +#define GL_EXT_convolution 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glConvolutionFilter1DEXT (GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *); +GLAPI void APIENTRY glConvolutionFilter2DEXT (GLenum, GLenum, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *); +GLAPI void APIENTRY glConvolutionParameterfEXT (GLenum, GLenum, GLfloat); +GLAPI void APIENTRY glConvolutionParameterfvEXT (GLenum, GLenum, const GLfloat *); +GLAPI void APIENTRY glConvolutionParameteriEXT (GLenum, GLenum, GLint); +GLAPI void APIENTRY glConvolutionParameterivEXT (GLenum, GLenum, const GLint *); +GLAPI void APIENTRY glCopyConvolutionFilter1DEXT (GLenum, GLenum, GLint, GLint, GLsizei); +GLAPI void APIENTRY glCopyConvolutionFilter2DEXT (GLenum, GLenum, GLint, GLint, GLsizei, GLsizei); +GLAPI void APIENTRY glGetConvolutionFilterEXT (GLenum, GLenum, GLenum, GLvoid *); +GLAPI void APIENTRY glGetConvolutionParameterfvEXT (GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glGetConvolutionParameterivEXT (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetSeparableFilterEXT (GLenum, GLenum, GLenum, GLvoid *, GLvoid *, GLvoid *); +GLAPI void APIENTRY glSeparableFilter2DEXT (GLenum, GLenum, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *, const GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, GLvoid *image); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSEPARABLEFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, GLvoid *row, GLvoid *column, GLvoid *span); +typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column); +#endif + +#ifndef GL_EXT_color_matrix +#define GL_EXT_color_matrix 1 +#endif + +#ifndef GL_SGI_color_table +#define GL_SGI_color_table 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTableSGI (GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *); +GLAPI void APIENTRY glColorTableParameterfvSGI (GLenum, GLenum, const GLfloat *); +GLAPI void APIENTRY glColorTableParameterivSGI (GLenum, GLenum, const GLint *); +GLAPI void APIENTRY glCopyColorTableSGI (GLenum, GLenum, GLint, GLint, GLsizei); +GLAPI void APIENTRY glGetColorTableSGI (GLenum, GLenum, GLenum, GLvoid *); +GLAPI void APIENTRY glGetColorTableParameterfvSGI (GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glGetColorTableParameterivSGI (GLenum, GLenum, GLint *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLGETCOLORTABLESGIPROC) (GLenum target, GLenum format, GLenum type, GLvoid *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, GLint *params); +#endif + +#ifndef GL_SGIX_pixel_texture +#define GL_SGIX_pixel_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTexGenSGIX (GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPIXELTEXGENSGIXPROC) (GLenum mode); +#endif + +#ifndef GL_SGIS_pixel_texture +#define GL_SGIS_pixel_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTexGenParameteriSGIS (GLenum, GLint); +GLAPI void APIENTRY glPixelTexGenParameterivSGIS (GLenum, const GLint *); +GLAPI void APIENTRY glPixelTexGenParameterfSGIS (GLenum, GLfloat); +GLAPI void APIENTRY glPixelTexGenParameterfvSGIS (GLenum, const GLfloat *); +GLAPI void APIENTRY glGetPixelTexGenParameterivSGIS (GLenum, GLint *); +GLAPI void APIENTRY glGetPixelTexGenParameterfvSGIS (GLenum, GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERISGISPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFSGISPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, GLfloat *params); +#endif + +#ifndef GL_SGIS_texture4D +#define GL_SGIS_texture4D 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage4DSGIS (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *); +GLAPI void APIENTRY glTexSubImage4DSGIS (GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXIMAGE4DSGISPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE4DSGISPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const GLvoid *pixels); +#endif + +#ifndef GL_SGI_texture_color_table +#define GL_SGI_texture_color_table 1 +#endif + +#ifndef GL_EXT_cmyka +#define GL_EXT_cmyka 1 +#endif + +#ifndef GL_EXT_texture_object +#define GL_EXT_texture_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glAreTexturesResidentEXT (GLsizei, const GLuint *, GLboolean *); +GLAPI void APIENTRY glBindTextureEXT (GLenum, GLuint); +GLAPI void APIENTRY glDeleteTexturesEXT (GLsizei, const GLuint *); +GLAPI void APIENTRY glGenTexturesEXT (GLsizei, GLuint *); +GLAPI GLboolean APIENTRY glIsTextureEXT (GLuint); +GLAPI void APIENTRY glPrioritizeTexturesEXT (GLsizei, const GLuint *, const GLclampf *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTEXTPROC) (GLsizei n, const GLuint *textures, GLboolean *residences); +typedef void (APIENTRYP PFNGLBINDTEXTUREEXTPROC) (GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLDELETETEXTURESEXTPROC) (GLsizei n, const GLuint *textures); +typedef void (APIENTRYP PFNGLGENTEXTURESEXTPROC) (GLsizei n, GLuint *textures); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREEXTPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESEXTPROC) (GLsizei n, const GLuint *textures, const GLclampf *priorities); +#endif + +#ifndef GL_SGIS_detail_texture +#define GL_SGIS_detail_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDetailTexFuncSGIS (GLenum, GLsizei, const GLfloat *); +GLAPI void APIENTRY glGetDetailTexFuncSGIS (GLenum, GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDETAILTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETDETAILTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +#endif + +#ifndef GL_SGIS_sharpen_texture +#define GL_SGIS_sharpen_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSharpenTexFuncSGIS (GLenum, GLsizei, const GLfloat *); +GLAPI void APIENTRY glGetSharpenTexFuncSGIS (GLenum, GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSHARPENTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETSHARPENTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +#endif + +#ifndef GL_EXT_packed_pixels +#define GL_EXT_packed_pixels 1 +#endif + +#ifndef GL_SGIS_texture_lod +#define GL_SGIS_texture_lod 1 +#endif + +#ifndef GL_SGIS_multisample +#define GL_SGIS_multisample 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleMaskSGIS (GLclampf, GLboolean); +GLAPI void APIENTRY glSamplePatternSGIS (GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSAMPLEMASKSGISPROC) (GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLSAMPLEPATTERNSGISPROC) (GLenum pattern); +#endif + +#ifndef GL_EXT_rescale_normal +#define GL_EXT_rescale_normal 1 +#endif + +#ifndef GL_EXT_vertex_array +#define GL_EXT_vertex_array 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glArrayElementEXT (GLint); +GLAPI void APIENTRY glColorPointerEXT (GLint, GLenum, GLsizei, GLsizei, const GLvoid *); +GLAPI void APIENTRY glDrawArraysEXT (GLenum, GLint, GLsizei); +GLAPI void APIENTRY glEdgeFlagPointerEXT (GLsizei, GLsizei, const GLboolean *); +GLAPI void APIENTRY glGetPointervEXT (GLenum, GLvoid* *); +GLAPI void APIENTRY glIndexPointerEXT (GLenum, GLsizei, GLsizei, const GLvoid *); +GLAPI void APIENTRY glNormalPointerEXT (GLenum, GLsizei, GLsizei, const GLvoid *); +GLAPI void APIENTRY glTexCoordPointerEXT (GLint, GLenum, GLsizei, GLsizei, const GLvoid *); +GLAPI void APIENTRY glVertexPointerEXT (GLint, GLenum, GLsizei, GLsizei, const GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLARRAYELEMENTEXTPROC) (GLint i); +typedef void (APIENTRYP PFNGLCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLDRAWARRAYSEXTPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTEREXTPROC) (GLsizei stride, GLsizei count, const GLboolean *pointer); +typedef void (APIENTRYP PFNGLGETPOINTERVEXTPROC) (GLenum pname, GLvoid* *params); +typedef void (APIENTRYP PFNGLINDEXPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLVERTEXPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +#endif + +#ifndef GL_EXT_misc_attribute +#define GL_EXT_misc_attribute 1 +#endif + +#ifndef GL_SGIS_generate_mipmap +#define GL_SGIS_generate_mipmap 1 +#endif + +#ifndef GL_SGIX_clipmap +#define GL_SGIX_clipmap 1 +#endif + +#ifndef GL_SGIX_shadow +#define GL_SGIX_shadow 1 +#endif + +#ifndef GL_SGIS_texture_edge_clamp +#define GL_SGIS_texture_edge_clamp 1 +#endif + +#ifndef GL_SGIS_texture_border_clamp +#define GL_SGIS_texture_border_clamp 1 +#endif + +#ifndef GL_EXT_blend_minmax +#define GL_EXT_blend_minmax 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationEXT (GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDEQUATIONEXTPROC) (GLenum mode); +#endif + +#ifndef GL_EXT_blend_subtract +#define GL_EXT_blend_subtract 1 +#endif + +#ifndef GL_EXT_blend_logic_op +#define GL_EXT_blend_logic_op 1 +#endif + +#ifndef GL_SGIX_interlace +#define GL_SGIX_interlace 1 +#endif + +#ifndef GL_SGIX_pixel_tiles +#define GL_SGIX_pixel_tiles 1 +#endif + +#ifndef GL_SGIX_texture_select +#define GL_SGIX_texture_select 1 +#endif + +#ifndef GL_SGIX_sprite +#define GL_SGIX_sprite 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSpriteParameterfSGIX (GLenum, GLfloat); +GLAPI void APIENTRY glSpriteParameterfvSGIX (GLenum, const GLfloat *); +GLAPI void APIENTRY glSpriteParameteriSGIX (GLenum, GLint); +GLAPI void APIENTRY glSpriteParameterivSGIX (GLenum, const GLint *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSPRITEPARAMETERFSGIXPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERFVSGIXPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERISGIXPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERIVSGIXPROC) (GLenum pname, const GLint *params); +#endif + +#ifndef GL_SGIX_texture_multi_buffer +#define GL_SGIX_texture_multi_buffer 1 +#endif + +#ifndef GL_EXT_point_parameters +#define GL_EXT_point_parameters 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfEXT (GLenum, GLfloat); +GLAPI void APIENTRY glPointParameterfvEXT (GLenum, const GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPOINTPARAMETERFEXTPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVEXTPROC) (GLenum pname, const GLfloat *params); +#endif + +#ifndef GL_SGIS_point_parameters +#define GL_SGIS_point_parameters 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfSGIS (GLenum, GLfloat); +GLAPI void APIENTRY glPointParameterfvSGIS (GLenum, const GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPOINTPARAMETERFSGISPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); +#endif + +#ifndef GL_SGIX_instruments +#define GL_SGIX_instruments 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLint APIENTRY glGetInstrumentsSGIX (void); +GLAPI void APIENTRY glInstrumentsBufferSGIX (GLsizei, GLint *); +GLAPI GLint APIENTRY glPollInstrumentsSGIX (GLint *); +GLAPI void APIENTRY glReadInstrumentsSGIX (GLint); +GLAPI void APIENTRY glStartInstrumentsSGIX (void); +GLAPI void APIENTRY glStopInstrumentsSGIX (GLint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLint (APIENTRYP PFNGLGETINSTRUMENTSSGIXPROC) (void); +typedef void (APIENTRYP PFNGLINSTRUMENTSBUFFERSGIXPROC) (GLsizei size, GLint *buffer); +typedef GLint (APIENTRYP PFNGLPOLLINSTRUMENTSSGIXPROC) (GLint *marker_p); +typedef void (APIENTRYP PFNGLREADINSTRUMENTSSGIXPROC) (GLint marker); +typedef void (APIENTRYP PFNGLSTARTINSTRUMENTSSGIXPROC) (void); +typedef void (APIENTRYP PFNGLSTOPINSTRUMENTSSGIXPROC) (GLint marker); +#endif + +#ifndef GL_SGIX_texture_scale_bias +#define GL_SGIX_texture_scale_bias 1 +#endif + +#ifndef GL_SGIX_framezoom +#define GL_SGIX_framezoom 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFrameZoomSGIX (GLint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFRAMEZOOMSGIXPROC) (GLint factor); +#endif + +#ifndef GL_SGIX_tag_sample_buffer +#define GL_SGIX_tag_sample_buffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTagSampleBufferSGIX (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTAGSAMPLEBUFFERSGIXPROC) (void); +#endif + +#ifndef GL_SGIX_polynomial_ffd +#define GL_SGIX_polynomial_ffd 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeformationMap3dSGIX (GLenum, GLdouble, GLdouble, GLint, GLint, GLdouble, GLdouble, GLint, GLint, GLdouble, GLdouble, GLint, GLint, const GLdouble *); +GLAPI void APIENTRY glDeformationMap3fSGIX (GLenum, GLfloat, GLfloat, GLint, GLint, GLfloat, GLfloat, GLint, GLint, GLfloat, GLfloat, GLint, GLint, const GLfloat *); +GLAPI void APIENTRY glDeformSGIX (GLbitfield); +GLAPI void APIENTRY glLoadIdentityDeformationMapSGIX (GLbitfield); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDEFORMATIONMAP3DSGIXPROC) (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points); +typedef void (APIENTRYP PFNGLDEFORMATIONMAP3FSGIXPROC) (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points); +typedef void (APIENTRYP PFNGLDEFORMSGIXPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC) (GLbitfield mask); +#endif + +#ifndef GL_SGIX_reference_plane +#define GL_SGIX_reference_plane 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReferencePlaneSGIX (const GLdouble *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLREFERENCEPLANESGIXPROC) (const GLdouble *equation); +#endif + +#ifndef GL_SGIX_flush_raster +#define GL_SGIX_flush_raster 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushRasterSGIX (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFLUSHRASTERSGIXPROC) (void); +#endif + +#ifndef GL_SGIX_depth_texture +#define GL_SGIX_depth_texture 1 +#endif + +#ifndef GL_SGIS_fog_function +#define GL_SGIS_fog_function 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFogFuncSGIS (GLsizei, const GLfloat *); +GLAPI void APIENTRY glGetFogFuncSGIS (GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFOGFUNCSGISPROC) (GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETFOGFUNCSGISPROC) (GLfloat *points); +#endif + +#ifndef GL_SGIX_fog_offset +#define GL_SGIX_fog_offset 1 +#endif + +#ifndef GL_HP_image_transform +#define GL_HP_image_transform 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glImageTransformParameteriHP (GLenum, GLenum, GLint); +GLAPI void APIENTRY glImageTransformParameterfHP (GLenum, GLenum, GLfloat); +GLAPI void APIENTRY glImageTransformParameterivHP (GLenum, GLenum, const GLint *); +GLAPI void APIENTRY glImageTransformParameterfvHP (GLenum, GLenum, const GLfloat *); +GLAPI void APIENTRY glGetImageTransformParameterivHP (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetImageTransformParameterfvHP (GLenum, GLenum, GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIHPPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFHPPROC) (GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, GLfloat *params); +#endif + +#ifndef GL_HP_convolution_border_modes +#define GL_HP_convolution_border_modes 1 +#endif + +#ifndef GL_SGIX_texture_add_env +#define GL_SGIX_texture_add_env 1 +#endif + +#ifndef GL_EXT_color_subtable +#define GL_EXT_color_subtable 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorSubTableEXT (GLenum, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *); +GLAPI void APIENTRY glCopyColorSubTableEXT (GLenum, GLsizei, GLint, GLint, GLsizei); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +#endif + +#ifndef GL_PGI_vertex_hints +#define GL_PGI_vertex_hints 1 +#endif + +#ifndef GL_PGI_misc_hints +#define GL_PGI_misc_hints 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glHintPGI (GLenum, GLint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLHINTPGIPROC) (GLenum target, GLint mode); +#endif + +#ifndef GL_EXT_paletted_texture +#define GL_EXT_paletted_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTableEXT (GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *); +GLAPI void APIENTRY glGetColorTableEXT (GLenum, GLenum, GLenum, GLvoid *); +GLAPI void APIENTRY glGetColorTableParameterivEXT (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetColorTableParameterfvEXT (GLenum, GLenum, GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOLORTABLEEXTPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEEXTPROC) (GLenum target, GLenum format, GLenum type, GLvoid *data); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +#endif + +#ifndef GL_EXT_clip_volume_hint +#define GL_EXT_clip_volume_hint 1 +#endif + +#ifndef GL_SGIX_list_priority +#define GL_SGIX_list_priority 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetListParameterfvSGIX (GLuint, GLenum, GLfloat *); +GLAPI void APIENTRY glGetListParameterivSGIX (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glListParameterfSGIX (GLuint, GLenum, GLfloat); +GLAPI void APIENTRY glListParameterfvSGIX (GLuint, GLenum, const GLfloat *); +GLAPI void APIENTRY glListParameteriSGIX (GLuint, GLenum, GLint); +GLAPI void APIENTRY glListParameterivSGIX (GLuint, GLenum, const GLint *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLLISTPARAMETERFSGIXPROC) (GLuint list, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLLISTPARAMETERISGIXPROC) (GLuint list, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, const GLint *params); +#endif + +#ifndef GL_SGIX_ir_instrument1 +#define GL_SGIX_ir_instrument1 1 +#endif + +#ifndef GL_SGIX_calligraphic_fragment +#define GL_SGIX_calligraphic_fragment 1 +#endif + +#ifndef GL_SGIX_texture_lod_bias +#define GL_SGIX_texture_lod_bias 1 +#endif + +#ifndef GL_SGIX_shadow_ambient +#define GL_SGIX_shadow_ambient 1 +#endif + +#ifndef GL_EXT_index_texture +#define GL_EXT_index_texture 1 +#endif + +#ifndef GL_EXT_index_material +#define GL_EXT_index_material 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIndexMaterialEXT (GLenum, GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLINDEXMATERIALEXTPROC) (GLenum face, GLenum mode); +#endif + +#ifndef GL_EXT_index_func +#define GL_EXT_index_func 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIndexFuncEXT (GLenum, GLclampf); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLINDEXFUNCEXTPROC) (GLenum func, GLclampf ref); +#endif + +#ifndef GL_EXT_index_array_formats +#define GL_EXT_index_array_formats 1 +#endif + +#ifndef GL_EXT_compiled_vertex_array +#define GL_EXT_compiled_vertex_array 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLockArraysEXT (GLint, GLsizei); +GLAPI void APIENTRY glUnlockArraysEXT (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLUNLOCKARRAYSEXTPROC) (void); +#endif + +#ifndef GL_EXT_cull_vertex +#define GL_EXT_cull_vertex 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCullParameterdvEXT (GLenum, GLdouble *); +GLAPI void APIENTRY glCullParameterfvEXT (GLenum, GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCULLPARAMETERDVEXTPROC) (GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLCULLPARAMETERFVEXTPROC) (GLenum pname, GLfloat *params); +#endif + +#ifndef GL_SGIX_ycrcb +#define GL_SGIX_ycrcb 1 +#endif + +#ifndef GL_SGIX_fragment_lighting +#define GL_SGIX_fragment_lighting 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFragmentColorMaterialSGIX (GLenum, GLenum); +GLAPI void APIENTRY glFragmentLightfSGIX (GLenum, GLenum, GLfloat); +GLAPI void APIENTRY glFragmentLightfvSGIX (GLenum, GLenum, const GLfloat *); +GLAPI void APIENTRY glFragmentLightiSGIX (GLenum, GLenum, GLint); +GLAPI void APIENTRY glFragmentLightivSGIX (GLenum, GLenum, const GLint *); +GLAPI void APIENTRY glFragmentLightModelfSGIX (GLenum, GLfloat); +GLAPI void APIENTRY glFragmentLightModelfvSGIX (GLenum, const GLfloat *); +GLAPI void APIENTRY glFragmentLightModeliSGIX (GLenum, GLint); +GLAPI void APIENTRY glFragmentLightModelivSGIX (GLenum, const GLint *); +GLAPI void APIENTRY glFragmentMaterialfSGIX (GLenum, GLenum, GLfloat); +GLAPI void APIENTRY glFragmentMaterialfvSGIX (GLenum, GLenum, const GLfloat *); +GLAPI void APIENTRY glFragmentMaterialiSGIX (GLenum, GLenum, GLint); +GLAPI void APIENTRY glFragmentMaterialivSGIX (GLenum, GLenum, const GLint *); +GLAPI void APIENTRY glGetFragmentLightfvSGIX (GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glGetFragmentLightivSGIX (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetFragmentMaterialfvSGIX (GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glGetFragmentMaterialivSGIX (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glLightEnviSGIX (GLenum, GLint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFRAGMENTCOLORMATERIALSGIXPROC) (GLenum face, GLenum mode); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFSGIXPROC) (GLenum light, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTISGIXPROC) (GLenum light, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFSGIXPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFVSGIXPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELISGIXPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELIVSGIXPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFSGIXPROC) (GLenum face, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALISGIXPROC) (GLenum face, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLLIGHTENVISGIXPROC) (GLenum pname, GLint param); +#endif + +#ifndef GL_IBM_rasterpos_clip +#define GL_IBM_rasterpos_clip 1 +#endif + +#ifndef GL_HP_texture_lighting +#define GL_HP_texture_lighting 1 +#endif + +#ifndef GL_EXT_draw_range_elements +#define GL_EXT_draw_range_elements 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawRangeElementsEXT (GLenum, GLuint, GLuint, GLsizei, GLenum, const GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSEXTPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); +#endif + +#ifndef GL_WIN_phong_shading +#define GL_WIN_phong_shading 1 +#endif + +#ifndef GL_WIN_specular_fog +#define GL_WIN_specular_fog 1 +#endif + +#ifndef GL_EXT_light_texture +#define GL_EXT_light_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glApplyTextureEXT (GLenum); +GLAPI void APIENTRY glTextureLightEXT (GLenum); +GLAPI void APIENTRY glTextureMaterialEXT (GLenum, GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLAPPLYTEXTUREEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLTEXTURELIGHTEXTPROC) (GLenum pname); +typedef void (APIENTRYP PFNGLTEXTUREMATERIALEXTPROC) (GLenum face, GLenum mode); +#endif + +#ifndef GL_SGIX_blend_alpha_minmax +#define GL_SGIX_blend_alpha_minmax 1 +#endif + +#ifndef GL_EXT_bgra +#define GL_EXT_bgra 1 +#endif + +#ifndef GL_SGIX_async +#define GL_SGIX_async 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glAsyncMarkerSGIX (GLuint); +GLAPI GLint APIENTRY glFinishAsyncSGIX (GLuint *); +GLAPI GLint APIENTRY glPollAsyncSGIX (GLuint *); +GLAPI GLuint APIENTRY glGenAsyncMarkersSGIX (GLsizei); +GLAPI void APIENTRY glDeleteAsyncMarkersSGIX (GLuint, GLsizei); +GLAPI GLboolean APIENTRY glIsAsyncMarkerSGIX (GLuint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLASYNCMARKERSGIXPROC) (GLuint marker); +typedef GLint (APIENTRYP PFNGLFINISHASYNCSGIXPROC) (GLuint *markerp); +typedef GLint (APIENTRYP PFNGLPOLLASYNCSGIXPROC) (GLuint *markerp); +typedef GLuint (APIENTRYP PFNGLGENASYNCMARKERSSGIXPROC) (GLsizei range); +typedef void (APIENTRYP PFNGLDELETEASYNCMARKERSSGIXPROC) (GLuint marker, GLsizei range); +typedef GLboolean (APIENTRYP PFNGLISASYNCMARKERSGIXPROC) (GLuint marker); +#endif + +#ifndef GL_SGIX_async_pixel +#define GL_SGIX_async_pixel 1 +#endif + +#ifndef GL_SGIX_async_histogram +#define GL_SGIX_async_histogram 1 +#endif + +#ifndef GL_INTEL_parallel_arrays +#define GL_INTEL_parallel_arrays 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexPointervINTEL (GLint, GLenum, const GLvoid* *); +GLAPI void APIENTRY glNormalPointervINTEL (GLenum, const GLvoid* *); +GLAPI void APIENTRY glColorPointervINTEL (GLint, GLenum, const GLvoid* *); +GLAPI void APIENTRY glTexCoordPointervINTEL (GLint, GLenum, const GLvoid* *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXPOINTERVINTELPROC) (GLint size, GLenum type, const GLvoid* *pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTERVINTELPROC) (GLenum type, const GLvoid* *pointer); +typedef void (APIENTRYP PFNGLCOLORPOINTERVINTELPROC) (GLint size, GLenum type, const GLvoid* *pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERVINTELPROC) (GLint size, GLenum type, const GLvoid* *pointer); +#endif + +#ifndef GL_HP_occlusion_test +#define GL_HP_occlusion_test 1 +#endif + +#ifndef GL_EXT_pixel_transform +#define GL_EXT_pixel_transform 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTransformParameteriEXT (GLenum, GLenum, GLint); +GLAPI void APIENTRY glPixelTransformParameterfEXT (GLenum, GLenum, GLfloat); +GLAPI void APIENTRY glPixelTransformParameterivEXT (GLenum, GLenum, const GLint *); +GLAPI void APIENTRY glPixelTransformParameterfvEXT (GLenum, GLenum, const GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); +#endif + +#ifndef GL_EXT_pixel_transform_color_table +#define GL_EXT_pixel_transform_color_table 1 +#endif + +#ifndef GL_EXT_shared_texture_palette +#define GL_EXT_shared_texture_palette 1 +#endif + +#ifndef GL_EXT_separate_specular_color +#define GL_EXT_separate_specular_color 1 +#endif + +#ifndef GL_EXT_secondary_color +#define GL_EXT_secondary_color 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSecondaryColor3bEXT (GLbyte, GLbyte, GLbyte); +GLAPI void APIENTRY glSecondaryColor3bvEXT (const GLbyte *); +GLAPI void APIENTRY glSecondaryColor3dEXT (GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glSecondaryColor3dvEXT (const GLdouble *); +GLAPI void APIENTRY glSecondaryColor3fEXT (GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glSecondaryColor3fvEXT (const GLfloat *); +GLAPI void APIENTRY glSecondaryColor3iEXT (GLint, GLint, GLint); +GLAPI void APIENTRY glSecondaryColor3ivEXT (const GLint *); +GLAPI void APIENTRY glSecondaryColor3sEXT (GLshort, GLshort, GLshort); +GLAPI void APIENTRY glSecondaryColor3svEXT (const GLshort *); +GLAPI void APIENTRY glSecondaryColor3ubEXT (GLubyte, GLubyte, GLubyte); +GLAPI void APIENTRY glSecondaryColor3ubvEXT (const GLubyte *); +GLAPI void APIENTRY glSecondaryColor3uiEXT (GLuint, GLuint, GLuint); +GLAPI void APIENTRY glSecondaryColor3uivEXT (const GLuint *); +GLAPI void APIENTRY glSecondaryColor3usEXT (GLushort, GLushort, GLushort); +GLAPI void APIENTRY glSecondaryColor3usvEXT (const GLushort *); +GLAPI void APIENTRY glSecondaryColorPointerEXT (GLint, GLenum, GLsizei, const GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BEXTPROC) (GLbyte red, GLbyte green, GLbyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DEXTPROC) (GLdouble red, GLdouble green, GLdouble blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FEXTPROC) (GLfloat red, GLfloat green, GLfloat blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IEXTPROC) (GLint red, GLint green, GLint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SEXTPROC) (GLshort red, GLshort green, GLshort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBEXTPROC) (GLubyte red, GLubyte green, GLubyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVEXTPROC) (const GLubyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIEXTPROC) (GLuint red, GLuint green, GLuint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVEXTPROC) (const GLuint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USEXTPROC) (GLushort red, GLushort green, GLushort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVEXTPROC) (const GLushort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +#ifndef GL_EXT_texture_perturb_normal +#define GL_EXT_texture_perturb_normal 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureNormalEXT (GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXTURENORMALEXTPROC) (GLenum mode); +#endif + +#ifndef GL_EXT_multi_draw_arrays +#define GL_EXT_multi_draw_arrays 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysEXT (GLenum, GLint *, GLsizei *, GLsizei); +GLAPI void APIENTRY glMultiDrawElementsEXT (GLenum, const GLsizei *, GLenum, const GLvoid* *, GLsizei); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount); +#endif + +#ifndef GL_EXT_fog_coord +#define GL_EXT_fog_coord 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFogCoordfEXT (GLfloat); +GLAPI void APIENTRY glFogCoordfvEXT (const GLfloat *); +GLAPI void APIENTRY glFogCoorddEXT (GLdouble); +GLAPI void APIENTRY glFogCoorddvEXT (const GLdouble *); +GLAPI void APIENTRY glFogCoordPointerEXT (GLenum, GLsizei, const GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFOGCOORDFEXTPROC) (GLfloat coord); +typedef void (APIENTRYP PFNGLFOGCOORDFVEXTPROC) (const GLfloat *coord); +typedef void (APIENTRYP PFNGLFOGCOORDDEXTPROC) (GLdouble coord); +typedef void (APIENTRYP PFNGLFOGCOORDDVEXTPROC) (const GLdouble *coord); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTEREXTPROC) (GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +#ifndef GL_REND_screen_coordinates +#define GL_REND_screen_coordinates 1 +#endif + +#ifndef GL_EXT_coordinate_frame +#define GL_EXT_coordinate_frame 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTangent3bEXT (GLbyte, GLbyte, GLbyte); +GLAPI void APIENTRY glTangent3bvEXT (const GLbyte *); +GLAPI void APIENTRY glTangent3dEXT (GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glTangent3dvEXT (const GLdouble *); +GLAPI void APIENTRY glTangent3fEXT (GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glTangent3fvEXT (const GLfloat *); +GLAPI void APIENTRY glTangent3iEXT (GLint, GLint, GLint); +GLAPI void APIENTRY glTangent3ivEXT (const GLint *); +GLAPI void APIENTRY glTangent3sEXT (GLshort, GLshort, GLshort); +GLAPI void APIENTRY glTangent3svEXT (const GLshort *); +GLAPI void APIENTRY glBinormal3bEXT (GLbyte, GLbyte, GLbyte); +GLAPI void APIENTRY glBinormal3bvEXT (const GLbyte *); +GLAPI void APIENTRY glBinormal3dEXT (GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glBinormal3dvEXT (const GLdouble *); +GLAPI void APIENTRY glBinormal3fEXT (GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glBinormal3fvEXT (const GLfloat *); +GLAPI void APIENTRY glBinormal3iEXT (GLint, GLint, GLint); +GLAPI void APIENTRY glBinormal3ivEXT (const GLint *); +GLAPI void APIENTRY glBinormal3sEXT (GLshort, GLshort, GLshort); +GLAPI void APIENTRY glBinormal3svEXT (const GLshort *); +GLAPI void APIENTRY glTangentPointerEXT (GLenum, GLsizei, const GLvoid *); +GLAPI void APIENTRY glBinormalPointerEXT (GLenum, GLsizei, const GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTANGENT3BEXTPROC) (GLbyte tx, GLbyte ty, GLbyte tz); +typedef void (APIENTRYP PFNGLTANGENT3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLTANGENT3DEXTPROC) (GLdouble tx, GLdouble ty, GLdouble tz); +typedef void (APIENTRYP PFNGLTANGENT3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLTANGENT3FEXTPROC) (GLfloat tx, GLfloat ty, GLfloat tz); +typedef void (APIENTRYP PFNGLTANGENT3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLTANGENT3IEXTPROC) (GLint tx, GLint ty, GLint tz); +typedef void (APIENTRYP PFNGLTANGENT3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLTANGENT3SEXTPROC) (GLshort tx, GLshort ty, GLshort tz); +typedef void (APIENTRYP PFNGLTANGENT3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLBINORMAL3BEXTPROC) (GLbyte bx, GLbyte by, GLbyte bz); +typedef void (APIENTRYP PFNGLBINORMAL3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLBINORMAL3DEXTPROC) (GLdouble bx, GLdouble by, GLdouble bz); +typedef void (APIENTRYP PFNGLBINORMAL3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLBINORMAL3FEXTPROC) (GLfloat bx, GLfloat by, GLfloat bz); +typedef void (APIENTRYP PFNGLBINORMAL3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLBINORMAL3IEXTPROC) (GLint bx, GLint by, GLint bz); +typedef void (APIENTRYP PFNGLBINORMAL3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLBINORMAL3SEXTPROC) (GLshort bx, GLshort by, GLshort bz); +typedef void (APIENTRYP PFNGLBINORMAL3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLTANGENTPOINTEREXTPROC) (GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLBINORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +#ifndef GL_EXT_texture_env_combine +#define GL_EXT_texture_env_combine 1 +#endif + +#ifndef GL_APPLE_specular_vector +#define GL_APPLE_specular_vector 1 +#endif + +#ifndef GL_APPLE_transform_hint +#define GL_APPLE_transform_hint 1 +#endif + +#ifndef GL_SGIX_fog_scale +#define GL_SGIX_fog_scale 1 +#endif + +#ifndef GL_SUNX_constant_data +#define GL_SUNX_constant_data 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFinishTextureSUNX (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFINISHTEXTURESUNXPROC) (void); +#endif + +#ifndef GL_SUN_global_alpha +#define GL_SUN_global_alpha 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGlobalAlphaFactorbSUN (GLbyte); +GLAPI void APIENTRY glGlobalAlphaFactorsSUN (GLshort); +GLAPI void APIENTRY glGlobalAlphaFactoriSUN (GLint); +GLAPI void APIENTRY glGlobalAlphaFactorfSUN (GLfloat); +GLAPI void APIENTRY glGlobalAlphaFactordSUN (GLdouble); +GLAPI void APIENTRY glGlobalAlphaFactorubSUN (GLubyte); +GLAPI void APIENTRY glGlobalAlphaFactorusSUN (GLushort); +GLAPI void APIENTRY glGlobalAlphaFactoruiSUN (GLuint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORBSUNPROC) (GLbyte factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORSSUNPROC) (GLshort factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORISUNPROC) (GLint factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORFSUNPROC) (GLfloat factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORDSUNPROC) (GLdouble factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUBSUNPROC) (GLubyte factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUSSUNPROC) (GLushort factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUISUNPROC) (GLuint factor); +#endif + +#ifndef GL_SUN_triangle_list +#define GL_SUN_triangle_list 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReplacementCodeuiSUN (GLuint); +GLAPI void APIENTRY glReplacementCodeusSUN (GLushort); +GLAPI void APIENTRY glReplacementCodeubSUN (GLubyte); +GLAPI void APIENTRY glReplacementCodeuivSUN (const GLuint *); +GLAPI void APIENTRY glReplacementCodeusvSUN (const GLushort *); +GLAPI void APIENTRY glReplacementCodeubvSUN (const GLubyte *); +GLAPI void APIENTRY glReplacementCodePointerSUN (GLenum, GLsizei, const GLvoid* *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUISUNPROC) (GLuint code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSSUNPROC) (GLushort code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBSUNPROC) (GLubyte code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVSUNPROC) (const GLuint *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSVSUNPROC) (const GLushort *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBVSUNPROC) (const GLubyte *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEPOINTERSUNPROC) (GLenum type, GLsizei stride, const GLvoid* *pointer); +#endif + +#ifndef GL_SUN_vertex +#define GL_SUN_vertex 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColor4ubVertex2fSUN (GLubyte, GLubyte, GLubyte, GLubyte, GLfloat, GLfloat); +GLAPI void APIENTRY glColor4ubVertex2fvSUN (const GLubyte *, const GLfloat *); +GLAPI void APIENTRY glColor4ubVertex3fSUN (GLubyte, GLubyte, GLubyte, GLubyte, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glColor4ubVertex3fvSUN (const GLubyte *, const GLfloat *); +GLAPI void APIENTRY glColor3fVertex3fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glColor3fVertex3fvSUN (const GLfloat *, const GLfloat *); +GLAPI void APIENTRY glNormal3fVertex3fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glNormal3fVertex3fvSUN (const GLfloat *, const GLfloat *); +GLAPI void APIENTRY glColor4fNormal3fVertex3fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glColor4fNormal3fVertex3fvSUN (const GLfloat *, const GLfloat *, const GLfloat *); +GLAPI void APIENTRY glTexCoord2fVertex3fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glTexCoord2fVertex3fvSUN (const GLfloat *, const GLfloat *); +GLAPI void APIENTRY glTexCoord4fVertex4fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glTexCoord4fVertex4fvSUN (const GLfloat *, const GLfloat *); +GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fSUN (GLfloat, GLfloat, GLubyte, GLubyte, GLubyte, GLubyte, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fvSUN (const GLfloat *, const GLubyte *, const GLfloat *); +GLAPI void APIENTRY glTexCoord2fColor3fVertex3fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glTexCoord2fColor3fVertex3fvSUN (const GLfloat *, const GLfloat *, const GLfloat *); +GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fvSUN (const GLfloat *, const GLfloat *, const GLfloat *); +GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fvSUN (const GLfloat *, const GLfloat *, const GLfloat *, const GLfloat *); +GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fvSUN (const GLfloat *, const GLfloat *, const GLfloat *, const GLfloat *); +GLAPI void APIENTRY glReplacementCodeuiVertex3fSUN (GLuint, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glReplacementCodeuiVertex3fvSUN (const GLuint *, const GLfloat *); +GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fSUN (GLuint, GLubyte, GLubyte, GLubyte, GLubyte, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fvSUN (const GLuint *, const GLubyte *, const GLfloat *); +GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fSUN (GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fvSUN (const GLuint *, const GLfloat *, const GLfloat *); +GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fSUN (GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fvSUN (const GLuint *, const GLfloat *, const GLfloat *); +GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fSUN (GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fvSUN (const GLuint *, const GLfloat *, const GLfloat *, const GLfloat *); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fSUN (GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fvSUN (const GLuint *, const GLfloat *, const GLfloat *); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN (GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN (const GLuint *, const GLfloat *, const GLfloat *, const GLfloat *); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN (GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN (const GLuint *, const GLfloat *, const GLfloat *, const GLfloat *, const GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FVSUNPROC) (const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FVSUNPROC) (const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FSUNPROC) (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC) (const GLfloat *tc, const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC) (GLuint rc, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC) (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC) (const GLuint *rc, const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +#endif + +#ifndef GL_EXT_blend_func_separate +#define GL_EXT_blend_func_separate 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparateEXT (GLenum, GLenum, GLenum, GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEEXTPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif + +#ifndef GL_INGR_blend_func_separate +#define GL_INGR_blend_func_separate 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparateINGR (GLenum, GLenum, GLenum, GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINGRPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif + +#ifndef GL_INGR_color_clamp +#define GL_INGR_color_clamp 1 +#endif + +#ifndef GL_INGR_interlace_read +#define GL_INGR_interlace_read 1 +#endif + +#ifndef GL_EXT_stencil_wrap +#define GL_EXT_stencil_wrap 1 +#endif + +#ifndef GL_EXT_422_pixels +#define GL_EXT_422_pixels 1 +#endif + +#ifndef GL_NV_texgen_reflection +#define GL_NV_texgen_reflection 1 +#endif + +#ifndef GL_SUN_convolution_border_modes +#define GL_SUN_convolution_border_modes 1 +#endif + +#ifndef GL_EXT_texture_env_add +#define GL_EXT_texture_env_add 1 +#endif + +#ifndef GL_EXT_texture_lod_bias +#define GL_EXT_texture_lod_bias 1 +#endif + +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_filter_anisotropic 1 +#endif + +#ifndef GL_EXT_vertex_weighting +#define GL_EXT_vertex_weighting 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexWeightfEXT (GLfloat); +GLAPI void APIENTRY glVertexWeightfvEXT (const GLfloat *); +GLAPI void APIENTRY glVertexWeightPointerEXT (GLsizei, GLenum, GLsizei, const GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXWEIGHTFEXTPROC) (GLfloat weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTFVEXTPROC) (const GLfloat *weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTPOINTEREXTPROC) (GLsizei size, GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +#ifndef GL_NV_light_max_exponent +#define GL_NV_light_max_exponent 1 +#endif + +#ifndef GL_NV_vertex_array_range +#define GL_NV_vertex_array_range 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushVertexArrayRangeNV (void); +GLAPI void APIENTRY glVertexArrayRangeNV (GLsizei, const GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGENVPROC) (void); +typedef void (APIENTRYP PFNGLVERTEXARRAYRANGENVPROC) (GLsizei length, const GLvoid *pointer); +#endif + +#ifndef GL_NV_register_combiners +#define GL_NV_register_combiners 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCombinerParameterfvNV (GLenum, const GLfloat *); +GLAPI void APIENTRY glCombinerParameterfNV (GLenum, GLfloat); +GLAPI void APIENTRY glCombinerParameterivNV (GLenum, const GLint *); +GLAPI void APIENTRY glCombinerParameteriNV (GLenum, GLint); +GLAPI void APIENTRY glCombinerInputNV (GLenum, GLenum, GLenum, GLenum, GLenum, GLenum); +GLAPI void APIENTRY glCombinerOutputNV (GLenum, GLenum, GLenum, GLenum, GLenum, GLenum, GLenum, GLboolean, GLboolean, GLboolean); +GLAPI void APIENTRY glFinalCombinerInputNV (GLenum, GLenum, GLenum, GLenum); +GLAPI void APIENTRY glGetCombinerInputParameterfvNV (GLenum, GLenum, GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glGetCombinerInputParameterivNV (GLenum, GLenum, GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetCombinerOutputParameterfvNV (GLenum, GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glGetCombinerOutputParameterivNV (GLenum, GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetFinalCombinerInputParameterfvNV (GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glGetFinalCombinerInputParameterivNV (GLenum, GLenum, GLint *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFVNVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFNVPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERIVNVPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERINVPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLCOMBINERINPUTNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (APIENTRYP PFNGLCOMBINEROUTPUTNVPROC) (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); +typedef void (APIENTRYP PFNGLFINALCOMBINERINPUTNVPROC) (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC) (GLenum variable, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC) (GLenum variable, GLenum pname, GLint *params); +#endif + +#ifndef GL_NV_fog_distance +#define GL_NV_fog_distance 1 +#endif + +#ifndef GL_NV_texgen_emboss +#define GL_NV_texgen_emboss 1 +#endif + +#ifndef GL_NV_blend_square +#define GL_NV_blend_square 1 +#endif + +#ifndef GL_NV_texture_env_combine4 +#define GL_NV_texture_env_combine4 1 +#endif + +#ifndef GL_MESA_resize_buffers +#define GL_MESA_resize_buffers 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glResizeBuffersMESA (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLRESIZEBUFFERSMESAPROC) (void); +#endif + +#ifndef GL_MESA_window_pos +#define GL_MESA_window_pos 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWindowPos2dMESA (GLdouble, GLdouble); +GLAPI void APIENTRY glWindowPos2dvMESA (const GLdouble *); +GLAPI void APIENTRY glWindowPos2fMESA (GLfloat, GLfloat); +GLAPI void APIENTRY glWindowPos2fvMESA (const GLfloat *); +GLAPI void APIENTRY glWindowPos2iMESA (GLint, GLint); +GLAPI void APIENTRY glWindowPos2ivMESA (const GLint *); +GLAPI void APIENTRY glWindowPos2sMESA (GLshort, GLshort); +GLAPI void APIENTRY glWindowPos2svMESA (const GLshort *); +GLAPI void APIENTRY glWindowPos3dMESA (GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glWindowPos3dvMESA (const GLdouble *); +GLAPI void APIENTRY glWindowPos3fMESA (GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glWindowPos3fvMESA (const GLfloat *); +GLAPI void APIENTRY glWindowPos3iMESA (GLint, GLint, GLint); +GLAPI void APIENTRY glWindowPos3ivMESA (const GLint *); +GLAPI void APIENTRY glWindowPos3sMESA (GLshort, GLshort, GLshort); +GLAPI void APIENTRY glWindowPos3svMESA (const GLshort *); +GLAPI void APIENTRY glWindowPos4dMESA (GLdouble, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glWindowPos4dvMESA (const GLdouble *); +GLAPI void APIENTRY glWindowPos4fMESA (GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glWindowPos4fvMESA (const GLfloat *); +GLAPI void APIENTRY glWindowPos4iMESA (GLint, GLint, GLint, GLint); +GLAPI void APIENTRY glWindowPos4ivMESA (const GLint *); +GLAPI void APIENTRY glWindowPos4sMESA (GLshort, GLshort, GLshort, GLshort); +GLAPI void APIENTRY glWindowPos4svMESA (const GLshort *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLWINDOWPOS2DMESAPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FMESAPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IMESAPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SMESAPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVMESAPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DMESAPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FMESAPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IMESAPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SMESAPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVMESAPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4DMESAPROC) (GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLWINDOWPOS4DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4FMESAPROC) (GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLWINDOWPOS4FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4IMESAPROC) (GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLWINDOWPOS4IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4SMESAPROC) (GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLWINDOWPOS4SVMESAPROC) (const GLshort *v); +#endif + +#ifndef GL_IBM_cull_vertex +#define GL_IBM_cull_vertex 1 +#endif + +#ifndef GL_IBM_multimode_draw_arrays +#define GL_IBM_multimode_draw_arrays 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiModeDrawArraysIBM (const GLenum *, const GLint *, const GLsizei *, GLsizei, GLint); +GLAPI void APIENTRY glMultiModeDrawElementsIBM (const GLenum *, const GLsizei *, GLenum, const GLvoid* const *, GLsizei, GLint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLMULTIMODEDRAWARRAYSIBMPROC) (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride); +typedef void (APIENTRYP PFNGLMULTIMODEDRAWELEMENTSIBMPROC) (const GLenum *mode, const GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei primcount, GLint modestride); +#endif + +#ifndef GL_IBM_vertex_array_lists +#define GL_IBM_vertex_array_lists 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorPointerListIBM (GLint, GLenum, GLint, const GLvoid* *, GLint); +GLAPI void APIENTRY glSecondaryColorPointerListIBM (GLint, GLenum, GLint, const GLvoid* *, GLint); +GLAPI void APIENTRY glEdgeFlagPointerListIBM (GLint, const GLboolean* *, GLint); +GLAPI void APIENTRY glFogCoordPointerListIBM (GLenum, GLint, const GLvoid* *, GLint); +GLAPI void APIENTRY glIndexPointerListIBM (GLenum, GLint, const GLvoid* *, GLint); +GLAPI void APIENTRY glNormalPointerListIBM (GLenum, GLint, const GLvoid* *, GLint); +GLAPI void APIENTRY glTexCoordPointerListIBM (GLint, GLenum, GLint, const GLvoid* *, GLint); +GLAPI void APIENTRY glVertexPointerListIBM (GLint, GLenum, GLint, const GLvoid* *, GLint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERLISTIBMPROC) (GLint stride, const GLboolean* *pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERLISTIBMPROC) (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLINDEXPOINTERLISTIBMPROC) (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLNORMALPOINTERLISTIBMPROC) (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLVERTEXPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride); +#endif + +#ifndef GL_SGIX_subsample +#define GL_SGIX_subsample 1 +#endif + +#ifndef GL_SGIX_ycrcba +#define GL_SGIX_ycrcba 1 +#endif + +#ifndef GL_SGIX_ycrcb_subsample +#define GL_SGIX_ycrcb_subsample 1 +#endif + +#ifndef GL_SGIX_depth_pass_instrument +#define GL_SGIX_depth_pass_instrument 1 +#endif + +#ifndef GL_3DFX_texture_compression_FXT1 +#define GL_3DFX_texture_compression_FXT1 1 +#endif + +#ifndef GL_3DFX_multisample +#define GL_3DFX_multisample 1 +#endif + +#ifndef GL_3DFX_tbuffer +#define GL_3DFX_tbuffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTbufferMask3DFX (GLuint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTBUFFERMASK3DFXPROC) (GLuint mask); +#endif + +#ifndef GL_EXT_multisample +#define GL_EXT_multisample 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleMaskEXT (GLclampf, GLboolean); +GLAPI void APIENTRY glSamplePatternEXT (GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSAMPLEMASKEXTPROC) (GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLSAMPLEPATTERNEXTPROC) (GLenum pattern); +#endif + +#ifndef GL_SGIX_vertex_preclip +#define GL_SGIX_vertex_preclip 1 +#endif + +#ifndef GL_SGIX_convolution_accuracy +#define GL_SGIX_convolution_accuracy 1 +#endif + +#ifndef GL_SGIX_resample +#define GL_SGIX_resample 1 +#endif + +#ifndef GL_SGIS_point_line_texgen +#define GL_SGIS_point_line_texgen 1 +#endif + +#ifndef GL_SGIS_texture_color_mask +#define GL_SGIS_texture_color_mask 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureColorMaskSGIS (GLboolean, GLboolean, GLboolean, GLboolean); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXTURECOLORMASKSGISPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +#endif + +#ifndef GL_SGIX_igloo_interface +#define GL_SGIX_igloo_interface 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIglooInterfaceSGIX (GLenum, const GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLIGLOOINTERFACESGIXPROC) (GLenum pname, const GLvoid *params); +#endif + +#ifndef GL_EXT_texture_env_dot3 +#define GL_EXT_texture_env_dot3 1 +#endif + +#ifndef GL_ATI_texture_mirror_once +#define GL_ATI_texture_mirror_once 1 +#endif + +#ifndef GL_NV_fence +#define GL_NV_fence 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeleteFencesNV (GLsizei, const GLuint *); +GLAPI void APIENTRY glGenFencesNV (GLsizei, GLuint *); +GLAPI GLboolean APIENTRY glIsFenceNV (GLuint); +GLAPI GLboolean APIENTRY glTestFenceNV (GLuint); +GLAPI void APIENTRY glGetFenceivNV (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glFinishFenceNV (GLuint); +GLAPI void APIENTRY glSetFenceNV (GLuint, GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDELETEFENCESNVPROC) (GLsizei n, const GLuint *fences); +typedef void (APIENTRYP PFNGLGENFENCESNVPROC) (GLsizei n, GLuint *fences); +typedef GLboolean (APIENTRYP PFNGLISFENCENVPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTFENCENVPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLGETFENCEIVNVPROC) (GLuint fence, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition); +#endif + +#ifndef GL_NV_evaluators +#define GL_NV_evaluators 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMapControlPointsNV (GLenum, GLuint, GLenum, GLsizei, GLsizei, GLint, GLint, GLboolean, const GLvoid *); +GLAPI void APIENTRY glMapParameterivNV (GLenum, GLenum, const GLint *); +GLAPI void APIENTRY glMapParameterfvNV (GLenum, GLenum, const GLfloat *); +GLAPI void APIENTRY glGetMapControlPointsNV (GLenum, GLuint, GLenum, GLsizei, GLsizei, GLboolean, GLvoid *); +GLAPI void APIENTRY glGetMapParameterivNV (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetMapParameterfvNV (GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glGetMapAttribParameterivNV (GLenum, GLuint, GLenum, GLint *); +GLAPI void APIENTRY glGetMapAttribParameterfvNV (GLenum, GLuint, GLenum, GLfloat *); +GLAPI void APIENTRY glEvalMapsNV (GLenum, GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const GLvoid *points); +typedef void (APIENTRYP PFNGLMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, GLvoid *points); +typedef void (APIENTRYP PFNGLGETMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERIVNVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLEVALMAPSNVPROC) (GLenum target, GLenum mode); +#endif + +#ifndef GL_NV_packed_depth_stencil +#define GL_NV_packed_depth_stencil 1 +#endif + +#ifndef GL_NV_register_combiners2 +#define GL_NV_register_combiners2 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCombinerStageParameterfvNV (GLenum, GLenum, const GLfloat *); +GLAPI void APIENTRY glGetCombinerStageParameterfvNV (GLenum, GLenum, GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, GLfloat *params); +#endif + +#ifndef GL_NV_texture_compression_vtc +#define GL_NV_texture_compression_vtc 1 +#endif + +#ifndef GL_NV_texture_rectangle +#define GL_NV_texture_rectangle 1 +#endif + +#ifndef GL_NV_texture_shader +#define GL_NV_texture_shader 1 +#endif + +#ifndef GL_NV_texture_shader2 +#define GL_NV_texture_shader2 1 +#endif + +#ifndef GL_NV_vertex_array_range2 +#define GL_NV_vertex_array_range2 1 +#endif + +#ifndef GL_NV_vertex_program +#define GL_NV_vertex_program 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glAreProgramsResidentNV (GLsizei, const GLuint *, GLboolean *); +GLAPI void APIENTRY glBindProgramNV (GLenum, GLuint); +GLAPI void APIENTRY glDeleteProgramsNV (GLsizei, const GLuint *); +GLAPI void APIENTRY glExecuteProgramNV (GLenum, GLuint, const GLfloat *); +GLAPI void APIENTRY glGenProgramsNV (GLsizei, GLuint *); +GLAPI void APIENTRY glGetProgramParameterdvNV (GLenum, GLuint, GLenum, GLdouble *); +GLAPI void APIENTRY glGetProgramParameterfvNV (GLenum, GLuint, GLenum, GLfloat *); +GLAPI void APIENTRY glGetProgramivNV (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glGetProgramStringNV (GLuint, GLenum, GLubyte *); +GLAPI void APIENTRY glGetTrackMatrixivNV (GLenum, GLuint, GLenum, GLint *); +GLAPI void APIENTRY glGetVertexAttribdvNV (GLuint, GLenum, GLdouble *); +GLAPI void APIENTRY glGetVertexAttribfvNV (GLuint, GLenum, GLfloat *); +GLAPI void APIENTRY glGetVertexAttribivNV (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glGetVertexAttribPointervNV (GLuint, GLenum, GLvoid* *); +GLAPI GLboolean APIENTRY glIsProgramNV (GLuint); +GLAPI void APIENTRY glLoadProgramNV (GLenum, GLuint, GLsizei, const GLubyte *); +GLAPI void APIENTRY glProgramParameter4dNV (GLenum, GLuint, GLdouble, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glProgramParameter4dvNV (GLenum, GLuint, const GLdouble *); +GLAPI void APIENTRY glProgramParameter4fNV (GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glProgramParameter4fvNV (GLenum, GLuint, const GLfloat *); +GLAPI void APIENTRY glProgramParameters4dvNV (GLenum, GLuint, GLuint, const GLdouble *); +GLAPI void APIENTRY glProgramParameters4fvNV (GLenum, GLuint, GLuint, const GLfloat *); +GLAPI void APIENTRY glRequestResidentProgramsNV (GLsizei, const GLuint *); +GLAPI void APIENTRY glTrackMatrixNV (GLenum, GLuint, GLenum, GLenum); +GLAPI void APIENTRY glVertexAttribPointerNV (GLuint, GLint, GLenum, GLsizei, const GLvoid *); +GLAPI void APIENTRY glVertexAttrib1dNV (GLuint, GLdouble); +GLAPI void APIENTRY glVertexAttrib1dvNV (GLuint, const GLdouble *); +GLAPI void APIENTRY glVertexAttrib1fNV (GLuint, GLfloat); +GLAPI void APIENTRY glVertexAttrib1fvNV (GLuint, const GLfloat *); +GLAPI void APIENTRY glVertexAttrib1sNV (GLuint, GLshort); +GLAPI void APIENTRY glVertexAttrib1svNV (GLuint, const GLshort *); +GLAPI void APIENTRY glVertexAttrib2dNV (GLuint, GLdouble, GLdouble); +GLAPI void APIENTRY glVertexAttrib2dvNV (GLuint, const GLdouble *); +GLAPI void APIENTRY glVertexAttrib2fNV (GLuint, GLfloat, GLfloat); +GLAPI void APIENTRY glVertexAttrib2fvNV (GLuint, const GLfloat *); +GLAPI void APIENTRY glVertexAttrib2sNV (GLuint, GLshort, GLshort); +GLAPI void APIENTRY glVertexAttrib2svNV (GLuint, const GLshort *); +GLAPI void APIENTRY glVertexAttrib3dNV (GLuint, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glVertexAttrib3dvNV (GLuint, const GLdouble *); +GLAPI void APIENTRY glVertexAttrib3fNV (GLuint, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glVertexAttrib3fvNV (GLuint, const GLfloat *); +GLAPI void APIENTRY glVertexAttrib3sNV (GLuint, GLshort, GLshort, GLshort); +GLAPI void APIENTRY glVertexAttrib3svNV (GLuint, const GLshort *); +GLAPI void APIENTRY glVertexAttrib4dNV (GLuint, GLdouble, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glVertexAttrib4dvNV (GLuint, const GLdouble *); +GLAPI void APIENTRY glVertexAttrib4fNV (GLuint, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glVertexAttrib4fvNV (GLuint, const GLfloat *); +GLAPI void APIENTRY glVertexAttrib4sNV (GLuint, GLshort, GLshort, GLshort, GLshort); +GLAPI void APIENTRY glVertexAttrib4svNV (GLuint, const GLshort *); +GLAPI void APIENTRY glVertexAttrib4ubNV (GLuint, GLubyte, GLubyte, GLubyte, GLubyte); +GLAPI void APIENTRY glVertexAttrib4ubvNV (GLuint, const GLubyte *); +GLAPI void APIENTRY glVertexAttribs1dvNV (GLuint, GLsizei, const GLdouble *); +GLAPI void APIENTRY glVertexAttribs1fvNV (GLuint, GLsizei, const GLfloat *); +GLAPI void APIENTRY glVertexAttribs1svNV (GLuint, GLsizei, const GLshort *); +GLAPI void APIENTRY glVertexAttribs2dvNV (GLuint, GLsizei, const GLdouble *); +GLAPI void APIENTRY glVertexAttribs2fvNV (GLuint, GLsizei, const GLfloat *); +GLAPI void APIENTRY glVertexAttribs2svNV (GLuint, GLsizei, const GLshort *); +GLAPI void APIENTRY glVertexAttribs3dvNV (GLuint, GLsizei, const GLdouble *); +GLAPI void APIENTRY glVertexAttribs3fvNV (GLuint, GLsizei, const GLfloat *); +GLAPI void APIENTRY glVertexAttribs3svNV (GLuint, GLsizei, const GLshort *); +GLAPI void APIENTRY glVertexAttribs4dvNV (GLuint, GLsizei, const GLdouble *); +GLAPI void APIENTRY glVertexAttribs4fvNV (GLuint, GLsizei, const GLfloat *); +GLAPI void APIENTRY glVertexAttribs4svNV (GLuint, GLsizei, const GLshort *); +GLAPI void APIENTRY glVertexAttribs4ubvNV (GLuint, GLsizei, const GLubyte *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLboolean (APIENTRYP PFNGLAREPROGRAMSRESIDENTNVPROC) (GLsizei n, const GLuint *programs, GLboolean *residences); +typedef void (APIENTRYP PFNGLBINDPROGRAMNVPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETEPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLEXECUTEPROGRAMNVPROC) (GLenum target, GLuint id, const GLfloat *params); +typedef void (APIENTRYP PFNGLGENPROGRAMSNVPROC) (GLsizei n, GLuint *programs); +typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERDVNVPROC) (GLenum target, GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMIVNVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGNVPROC) (GLuint id, GLenum pname, GLubyte *program); +typedef void (APIENTRYP PFNGLGETTRACKMATRIXIVNVPROC) (GLenum target, GLuint address, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVNVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVNVPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVNVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVNVPROC) (GLuint index, GLenum pname, GLvoid* *pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLLOADPROGRAMNVPROC) (GLenum target, GLuint id, GLsizei len, const GLubyte *program); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DNVPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DVNVPROC) (GLenum target, GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FNVPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FVNVPROC) (GLenum target, GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4DVNVPROC) (GLenum target, GLuint index, GLuint count, const GLdouble *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4FVNVPROC) (GLenum target, GLuint index, GLuint count, const GLfloat *v); +typedef void (APIENTRYP PFNGLREQUESTRESIDENTPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLTRACKMATRIXNVPROC) (GLenum target, GLuint address, GLenum matrix, GLenum transform); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERNVPROC) (GLuint index, GLint fsize, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DNVPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FNVPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SNVPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DNVPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FNVPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SNVPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBNVPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVNVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4UBVNVPROC) (GLuint index, GLsizei count, const GLubyte *v); +#endif + +#ifndef GL_SGIX_texture_coordinate_clamp +#define GL_SGIX_texture_coordinate_clamp 1 +#endif + +#ifndef GL_SGIX_scalebias_hint +#define GL_SGIX_scalebias_hint 1 +#endif + +#ifndef GL_OML_interlace +#define GL_OML_interlace 1 +#endif + +#ifndef GL_OML_subsample +#define GL_OML_subsample 1 +#endif + +#ifndef GL_OML_resample +#define GL_OML_resample 1 +#endif + +#ifndef GL_NV_copy_depth_to_color +#define GL_NV_copy_depth_to_color 1 +#endif + +#ifndef GL_ATI_envmap_bumpmap +#define GL_ATI_envmap_bumpmap 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBumpParameterivATI (GLenum, const GLint *); +GLAPI void APIENTRY glTexBumpParameterfvATI (GLenum, const GLfloat *); +GLAPI void APIENTRY glGetTexBumpParameterivATI (GLenum, GLint *); +GLAPI void APIENTRY glGetTexBumpParameterfvATI (GLenum, GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERIVATIPROC) (GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERFVATIPROC) (GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERIVATIPROC) (GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERFVATIPROC) (GLenum pname, GLfloat *param); +#endif + +#ifndef GL_ATI_fragment_shader +#define GL_ATI_fragment_shader 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glGenFragmentShadersATI (GLuint); +GLAPI void APIENTRY glBindFragmentShaderATI (GLuint); +GLAPI void APIENTRY glDeleteFragmentShaderATI (GLuint); +GLAPI void APIENTRY glBeginFragmentShaderATI (void); +GLAPI void APIENTRY glEndFragmentShaderATI (void); +GLAPI void APIENTRY glPassTexCoordATI (GLuint, GLuint, GLenum); +GLAPI void APIENTRY glSampleMapATI (GLuint, GLuint, GLenum); +GLAPI void APIENTRY glColorFragmentOp1ATI (GLenum, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint); +GLAPI void APIENTRY glColorFragmentOp2ATI (GLenum, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint); +GLAPI void APIENTRY glColorFragmentOp3ATI (GLenum, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint); +GLAPI void APIENTRY glAlphaFragmentOp1ATI (GLenum, GLuint, GLuint, GLuint, GLuint, GLuint); +GLAPI void APIENTRY glAlphaFragmentOp2ATI (GLenum, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint); +GLAPI void APIENTRY glAlphaFragmentOp3ATI (GLenum, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint); +GLAPI void APIENTRY glSetFragmentShaderConstantATI (GLuint, const GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLuint (APIENTRYP PFNGLGENFRAGMENTSHADERSATIPROC) (GLuint range); +typedef void (APIENTRYP PFNGLBINDFRAGMENTSHADERATIPROC) (GLuint id); +typedef void (APIENTRYP PFNGLDELETEFRAGMENTSHADERATIPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINFRAGMENTSHADERATIPROC) (void); +typedef void (APIENTRYP PFNGLENDFRAGMENTSHADERATIPROC) (void); +typedef void (APIENTRYP PFNGLPASSTEXCOORDATIPROC) (GLuint dst, GLuint coord, GLenum swizzle); +typedef void (APIENTRYP PFNGLSAMPLEMAPATIPROC) (GLuint dst, GLuint interp, GLenum swizzle); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +typedef void (APIENTRYP PFNGLSETFRAGMENTSHADERCONSTANTATIPROC) (GLuint dst, const GLfloat *value); +#endif + +#ifndef GL_ATI_pn_triangles +#define GL_ATI_pn_triangles 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPNTrianglesiATI (GLenum, GLint); +GLAPI void APIENTRY glPNTrianglesfATI (GLenum, GLfloat); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPNTRIANGLESIATIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPNTRIANGLESFATIPROC) (GLenum pname, GLfloat param); +#endif + +#ifndef GL_ATI_vertex_array_object +#define GL_ATI_vertex_array_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glNewObjectBufferATI (GLsizei, const GLvoid *, GLenum); +GLAPI GLboolean APIENTRY glIsObjectBufferATI (GLuint); +GLAPI void APIENTRY glUpdateObjectBufferATI (GLuint, GLuint, GLsizei, const GLvoid *, GLenum); +GLAPI void APIENTRY glGetObjectBufferfvATI (GLuint, GLenum, GLfloat *); +GLAPI void APIENTRY glGetObjectBufferivATI (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glFreeObjectBufferATI (GLuint); +GLAPI void APIENTRY glArrayObjectATI (GLenum, GLint, GLenum, GLsizei, GLuint, GLuint); +GLAPI void APIENTRY glGetArrayObjectfvATI (GLenum, GLenum, GLfloat *); +GLAPI void APIENTRY glGetArrayObjectivATI (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glVariantArrayObjectATI (GLuint, GLenum, GLsizei, GLuint, GLuint); +GLAPI void APIENTRY glGetVariantArrayObjectfvATI (GLuint, GLenum, GLfloat *); +GLAPI void APIENTRY glGetVariantArrayObjectivATI (GLuint, GLenum, GLint *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLuint (APIENTRYP PFNGLNEWOBJECTBUFFERATIPROC) (GLsizei size, const GLvoid *pointer, GLenum usage); +typedef GLboolean (APIENTRYP PFNGLISOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLUPDATEOBJECTBUFFERATIPROC) (GLuint buffer, GLuint offset, GLsizei size, const GLvoid *pointer, GLenum preserve); +typedef void (APIENTRYP PFNGLGETOBJECTBUFFERFVATIPROC) (GLuint buffer, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETOBJECTBUFFERIVATIPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLFREEOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLARRAYOBJECTATIPROC) (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETARRAYOBJECTFVATIPROC) (GLenum array, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETARRAYOBJECTIVATIPROC) (GLenum array, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLVARIANTARRAYOBJECTATIPROC) (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTFVATIPROC) (GLuint id, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTIVATIPROC) (GLuint id, GLenum pname, GLint *params); +#endif + +#ifndef GL_EXT_vertex_shader +#define GL_EXT_vertex_shader 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginVertexShaderEXT (void); +GLAPI void APIENTRY glEndVertexShaderEXT (void); +GLAPI void APIENTRY glBindVertexShaderEXT (GLuint); +GLAPI GLuint APIENTRY glGenVertexShadersEXT (GLuint); +GLAPI void APIENTRY glDeleteVertexShaderEXT (GLuint); +GLAPI void APIENTRY glShaderOp1EXT (GLenum, GLuint, GLuint); +GLAPI void APIENTRY glShaderOp2EXT (GLenum, GLuint, GLuint, GLuint); +GLAPI void APIENTRY glShaderOp3EXT (GLenum, GLuint, GLuint, GLuint, GLuint); +GLAPI void APIENTRY glSwizzleEXT (GLuint, GLuint, GLenum, GLenum, GLenum, GLenum); +GLAPI void APIENTRY glWriteMaskEXT (GLuint, GLuint, GLenum, GLenum, GLenum, GLenum); +GLAPI void APIENTRY glInsertComponentEXT (GLuint, GLuint, GLuint); +GLAPI void APIENTRY glExtractComponentEXT (GLuint, GLuint, GLuint); +GLAPI GLuint APIENTRY glGenSymbolsEXT (GLenum, GLenum, GLenum, GLuint); +GLAPI void APIENTRY glSetInvariantEXT (GLuint, GLenum, const GLvoid *); +GLAPI void APIENTRY glSetLocalConstantEXT (GLuint, GLenum, const GLvoid *); +GLAPI void APIENTRY glVariantbvEXT (GLuint, const GLbyte *); +GLAPI void APIENTRY glVariantsvEXT (GLuint, const GLshort *); +GLAPI void APIENTRY glVariantivEXT (GLuint, const GLint *); +GLAPI void APIENTRY glVariantfvEXT (GLuint, const GLfloat *); +GLAPI void APIENTRY glVariantdvEXT (GLuint, const GLdouble *); +GLAPI void APIENTRY glVariantubvEXT (GLuint, const GLubyte *); +GLAPI void APIENTRY glVariantusvEXT (GLuint, const GLushort *); +GLAPI void APIENTRY glVariantuivEXT (GLuint, const GLuint *); +GLAPI void APIENTRY glVariantPointerEXT (GLuint, GLenum, GLuint, const GLvoid *); +GLAPI void APIENTRY glEnableVariantClientStateEXT (GLuint); +GLAPI void APIENTRY glDisableVariantClientStateEXT (GLuint); +GLAPI GLuint APIENTRY glBindLightParameterEXT (GLenum, GLenum); +GLAPI GLuint APIENTRY glBindMaterialParameterEXT (GLenum, GLenum); +GLAPI GLuint APIENTRY glBindTexGenParameterEXT (GLenum, GLenum, GLenum); +GLAPI GLuint APIENTRY glBindTextureUnitParameterEXT (GLenum, GLenum); +GLAPI GLuint APIENTRY glBindParameterEXT (GLenum); +GLAPI GLboolean APIENTRY glIsVariantEnabledEXT (GLuint, GLenum); +GLAPI void APIENTRY glGetVariantBooleanvEXT (GLuint, GLenum, GLboolean *); +GLAPI void APIENTRY glGetVariantIntegervEXT (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glGetVariantFloatvEXT (GLuint, GLenum, GLfloat *); +GLAPI void APIENTRY glGetVariantPointervEXT (GLuint, GLenum, GLvoid* *); +GLAPI void APIENTRY glGetInvariantBooleanvEXT (GLuint, GLenum, GLboolean *); +GLAPI void APIENTRY glGetInvariantIntegervEXT (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glGetInvariantFloatvEXT (GLuint, GLenum, GLfloat *); +GLAPI void APIENTRY glGetLocalConstantBooleanvEXT (GLuint, GLenum, GLboolean *); +GLAPI void APIENTRY glGetLocalConstantIntegervEXT (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glGetLocalConstantFloatvEXT (GLuint, GLenum, GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBEGINVERTEXSHADEREXTPROC) (void); +typedef void (APIENTRYP PFNGLENDVERTEXSHADEREXTPROC) (void); +typedef void (APIENTRYP PFNGLBINDVERTEXSHADEREXTPROC) (GLuint id); +typedef GLuint (APIENTRYP PFNGLGENVERTEXSHADERSEXTPROC) (GLuint range); +typedef void (APIENTRYP PFNGLDELETEVERTEXSHADEREXTPROC) (GLuint id); +typedef void (APIENTRYP PFNGLSHADEROP1EXTPROC) (GLenum op, GLuint res, GLuint arg1); +typedef void (APIENTRYP PFNGLSHADEROP2EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2); +typedef void (APIENTRYP PFNGLSHADEROP3EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); +typedef void (APIENTRYP PFNGLSWIZZLEEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +typedef void (APIENTRYP PFNGLWRITEMASKEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +typedef void (APIENTRYP PFNGLINSERTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); +typedef void (APIENTRYP PFNGLEXTRACTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); +typedef GLuint (APIENTRYP PFNGLGENSYMBOLSEXTPROC) (GLenum datatype, GLenum storagetype, GLenum range, GLuint components); +typedef void (APIENTRYP PFNGLSETINVARIANTEXTPROC) (GLuint id, GLenum type, const GLvoid *addr); +typedef void (APIENTRYP PFNGLSETLOCALCONSTANTEXTPROC) (GLuint id, GLenum type, const GLvoid *addr); +typedef void (APIENTRYP PFNGLVARIANTBVEXTPROC) (GLuint id, const GLbyte *addr); +typedef void (APIENTRYP PFNGLVARIANTSVEXTPROC) (GLuint id, const GLshort *addr); +typedef void (APIENTRYP PFNGLVARIANTIVEXTPROC) (GLuint id, const GLint *addr); +typedef void (APIENTRYP PFNGLVARIANTFVEXTPROC) (GLuint id, const GLfloat *addr); +typedef void (APIENTRYP PFNGLVARIANTDVEXTPROC) (GLuint id, const GLdouble *addr); +typedef void (APIENTRYP PFNGLVARIANTUBVEXTPROC) (GLuint id, const GLubyte *addr); +typedef void (APIENTRYP PFNGLVARIANTUSVEXTPROC) (GLuint id, const GLushort *addr); +typedef void (APIENTRYP PFNGLVARIANTUIVEXTPROC) (GLuint id, const GLuint *addr); +typedef void (APIENTRYP PFNGLVARIANTPOINTEREXTPROC) (GLuint id, GLenum type, GLuint stride, const GLvoid *addr); +typedef void (APIENTRYP PFNGLENABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); +typedef void (APIENTRYP PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); +typedef GLuint (APIENTRYP PFNGLBINDLIGHTPARAMETEREXTPROC) (GLenum light, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDMATERIALPARAMETEREXTPROC) (GLenum face, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDTEXGENPARAMETEREXTPROC) (GLenum unit, GLenum coord, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDTEXTUREUNITPARAMETEREXTPROC) (GLenum unit, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDPARAMETEREXTPROC) (GLenum value); +typedef GLboolean (APIENTRYP PFNGLISVARIANTENABLEDEXTPROC) (GLuint id, GLenum cap); +typedef void (APIENTRYP PFNGLGETVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +typedef void (APIENTRYP PFNGLGETVARIANTPOINTERVEXTPROC) (GLuint id, GLenum value, GLvoid* *data); +typedef void (APIENTRYP PFNGLGETINVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETINVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +#endif + +#ifndef GL_ATI_vertex_streams +#define GL_ATI_vertex_streams 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexStream1sATI (GLenum, GLshort); +GLAPI void APIENTRY glVertexStream1svATI (GLenum, const GLshort *); +GLAPI void APIENTRY glVertexStream1iATI (GLenum, GLint); +GLAPI void APIENTRY glVertexStream1ivATI (GLenum, const GLint *); +GLAPI void APIENTRY glVertexStream1fATI (GLenum, GLfloat); +GLAPI void APIENTRY glVertexStream1fvATI (GLenum, const GLfloat *); +GLAPI void APIENTRY glVertexStream1dATI (GLenum, GLdouble); +GLAPI void APIENTRY glVertexStream1dvATI (GLenum, const GLdouble *); +GLAPI void APIENTRY glVertexStream2sATI (GLenum, GLshort, GLshort); +GLAPI void APIENTRY glVertexStream2svATI (GLenum, const GLshort *); +GLAPI void APIENTRY glVertexStream2iATI (GLenum, GLint, GLint); +GLAPI void APIENTRY glVertexStream2ivATI (GLenum, const GLint *); +GLAPI void APIENTRY glVertexStream2fATI (GLenum, GLfloat, GLfloat); +GLAPI void APIENTRY glVertexStream2fvATI (GLenum, const GLfloat *); +GLAPI void APIENTRY glVertexStream2dATI (GLenum, GLdouble, GLdouble); +GLAPI void APIENTRY glVertexStream2dvATI (GLenum, const GLdouble *); +GLAPI void APIENTRY glVertexStream3sATI (GLenum, GLshort, GLshort, GLshort); +GLAPI void APIENTRY glVertexStream3svATI (GLenum, const GLshort *); +GLAPI void APIENTRY glVertexStream3iATI (GLenum, GLint, GLint, GLint); +GLAPI void APIENTRY glVertexStream3ivATI (GLenum, const GLint *); +GLAPI void APIENTRY glVertexStream3fATI (GLenum, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glVertexStream3fvATI (GLenum, const GLfloat *); +GLAPI void APIENTRY glVertexStream3dATI (GLenum, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glVertexStream3dvATI (GLenum, const GLdouble *); +GLAPI void APIENTRY glVertexStream4sATI (GLenum, GLshort, GLshort, GLshort, GLshort); +GLAPI void APIENTRY glVertexStream4svATI (GLenum, const GLshort *); +GLAPI void APIENTRY glVertexStream4iATI (GLenum, GLint, GLint, GLint, GLint); +GLAPI void APIENTRY glVertexStream4ivATI (GLenum, const GLint *); +GLAPI void APIENTRY glVertexStream4fATI (GLenum, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glVertexStream4fvATI (GLenum, const GLfloat *); +GLAPI void APIENTRY glVertexStream4dATI (GLenum, GLdouble, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glVertexStream4dvATI (GLenum, const GLdouble *); +GLAPI void APIENTRY glNormalStream3bATI (GLenum, GLbyte, GLbyte, GLbyte); +GLAPI void APIENTRY glNormalStream3bvATI (GLenum, const GLbyte *); +GLAPI void APIENTRY glNormalStream3sATI (GLenum, GLshort, GLshort, GLshort); +GLAPI void APIENTRY glNormalStream3svATI (GLenum, const GLshort *); +GLAPI void APIENTRY glNormalStream3iATI (GLenum, GLint, GLint, GLint); +GLAPI void APIENTRY glNormalStream3ivATI (GLenum, const GLint *); +GLAPI void APIENTRY glNormalStream3fATI (GLenum, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glNormalStream3fvATI (GLenum, const GLfloat *); +GLAPI void APIENTRY glNormalStream3dATI (GLenum, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glNormalStream3dvATI (GLenum, const GLdouble *); +GLAPI void APIENTRY glClientActiveVertexStreamATI (GLenum); +GLAPI void APIENTRY glVertexBlendEnviATI (GLenum, GLint); +GLAPI void APIENTRY glVertexBlendEnvfATI (GLenum, GLfloat); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXSTREAM1SATIPROC) (GLenum stream, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1IATIPROC) (GLenum stream, GLint x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1FATIPROC) (GLenum stream, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1DATIPROC) (GLenum stream, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2SATIPROC) (GLenum stream, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2IATIPROC) (GLenum stream, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2FATIPROC) (GLenum stream, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2DATIPROC) (GLenum stream, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3IATIPROC) (GLenum stream, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4IATIPROC) (GLenum stream, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3BATIPROC) (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3BVATIPROC) (GLenum stream, const GLbyte *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3SATIPROC) (GLenum stream, GLshort nx, GLshort ny, GLshort nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3IATIPROC) (GLenum stream, GLint nx, GLint ny, GLint nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3FATIPROC) (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3DATIPROC) (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC) (GLenum stream); +typedef void (APIENTRYP PFNGLVERTEXBLENDENVIATIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLVERTEXBLENDENVFATIPROC) (GLenum pname, GLfloat param); +#endif + +#ifndef GL_ATI_element_array +#define GL_ATI_element_array 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glElementPointerATI (GLenum, const GLvoid *); +GLAPI void APIENTRY glDrawElementArrayATI (GLenum, GLsizei); +GLAPI void APIENTRY glDrawRangeElementArrayATI (GLenum, GLuint, GLuint, GLsizei); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLELEMENTPOINTERATIPROC) (GLenum type, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYATIPROC) (GLenum mode, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYATIPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count); +#endif + +#ifndef GL_SUN_mesh_array +#define GL_SUN_mesh_array 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawMeshArraysSUN (GLenum, GLint, GLsizei, GLsizei); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWMESHARRAYSSUNPROC) (GLenum mode, GLint first, GLsizei count, GLsizei width); +#endif + +#ifndef GL_SUN_slice_accum +#define GL_SUN_slice_accum 1 +#endif + +#ifndef GL_NV_multisample_filter_hint +#define GL_NV_multisample_filter_hint 1 +#endif + +#ifndef GL_NV_depth_clamp +#define GL_NV_depth_clamp 1 +#endif + +#ifndef GL_NV_occlusion_query +#define GL_NV_occlusion_query 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenOcclusionQueriesNV (GLsizei, GLuint *); +GLAPI void APIENTRY glDeleteOcclusionQueriesNV (GLsizei, const GLuint *); +GLAPI GLboolean APIENTRY glIsOcclusionQueryNV (GLuint); +GLAPI void APIENTRY glBeginOcclusionQueryNV (GLuint); +GLAPI void APIENTRY glEndOcclusionQueryNV (void); +GLAPI void APIENTRY glGetOcclusionQueryivNV (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glGetOcclusionQueryuivNV (GLuint, GLenum, GLuint *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGENOCCLUSIONQUERIESNVPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEOCCLUSIONQUERIESNVPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISOCCLUSIONQUERYNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINOCCLUSIONQUERYNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLENDOCCLUSIONQUERYNVPROC) (void); +typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYIVNVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYUIVNVPROC) (GLuint id, GLenum pname, GLuint *params); +#endif + +#ifndef GL_NV_point_sprite +#define GL_NV_point_sprite 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameteriNV (GLenum, GLint); +GLAPI void APIENTRY glPointParameterivNV (GLenum, const GLint *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPOINTPARAMETERINVPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVNVPROC) (GLenum pname, const GLint *params); +#endif + +#ifndef GL_NV_texture_shader3 +#define GL_NV_texture_shader3 1 +#endif + +#ifndef GL_NV_vertex_program1_1 +#define GL_NV_vertex_program1_1 1 +#endif + +#ifndef GL_EXT_shadow_funcs +#define GL_EXT_shadow_funcs 1 +#endif + +#ifndef GL_EXT_stencil_two_side +#define GL_EXT_stencil_two_side 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveStencilFaceEXT (GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face); +#endif + +#ifndef GL_ATI_text_fragment_shader +#define GL_ATI_text_fragment_shader 1 +#endif + +#ifndef GL_APPLE_client_storage +#define GL_APPLE_client_storage 1 +#endif + +#ifndef GL_APPLE_element_array +#define GL_APPLE_element_array 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glElementPointerAPPLE (GLenum, const GLvoid *); +GLAPI void APIENTRY glDrawElementArrayAPPLE (GLenum, GLint, GLsizei); +GLAPI void APIENTRY glDrawRangeElementArrayAPPLE (GLenum, GLuint, GLuint, GLint, GLsizei); +GLAPI void APIENTRY glMultiDrawElementArrayAPPLE (GLenum, const GLint *, const GLsizei *, GLsizei); +GLAPI void APIENTRY glMultiDrawRangeElementArrayAPPLE (GLenum, GLuint, GLuint, const GLint *, const GLsizei *, GLsizei); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLELEMENTPOINTERAPPLEPROC) (GLenum type, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount); +#endif + +#ifndef GL_APPLE_fence +#define GL_APPLE_fence 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenFencesAPPLE (GLsizei, GLuint *); +GLAPI void APIENTRY glDeleteFencesAPPLE (GLsizei, const GLuint *); +GLAPI void APIENTRY glSetFenceAPPLE (GLuint); +GLAPI GLboolean APIENTRY glIsFenceAPPLE (GLuint); +GLAPI GLboolean APIENTRY glTestFenceAPPLE (GLuint); +GLAPI void APIENTRY glFinishFenceAPPLE (GLuint); +GLAPI GLboolean APIENTRY glTestObjectAPPLE (GLenum, GLuint); +GLAPI void APIENTRY glFinishObjectAPPLE (GLenum, GLint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGENFENCESAPPLEPROC) (GLsizei n, GLuint *fences); +typedef void (APIENTRYP PFNGLDELETEFENCESAPPLEPROC) (GLsizei n, const GLuint *fences); +typedef void (APIENTRYP PFNGLSETFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLISFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTFENCEAPPLEPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLFINISHFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTOBJECTAPPLEPROC) (GLenum object, GLuint name); +typedef void (APIENTRYP PFNGLFINISHOBJECTAPPLEPROC) (GLenum object, GLint name); +#endif + +#ifndef GL_APPLE_vertex_array_object +#define GL_APPLE_vertex_array_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindVertexArrayAPPLE (GLuint); +GLAPI void APIENTRY glDeleteVertexArraysAPPLE (GLsizei, const GLuint *); +GLAPI void APIENTRY glGenVertexArraysAPPLE (GLsizei, const GLuint *); +GLAPI GLboolean APIENTRY glIsVertexArrayAPPLE (GLuint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYAPPLEPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSAPPLEPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSAPPLEPROC) (GLsizei n, const GLuint *arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYAPPLEPROC) (GLuint array); +#endif + +#ifndef GL_APPLE_vertex_array_range +#define GL_APPLE_vertex_array_range 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexArrayRangeAPPLE (GLsizei, GLvoid *); +GLAPI void APIENTRY glFlushVertexArrayRangeAPPLE (GLsizei, GLvoid *); +GLAPI void APIENTRY glVertexArrayParameteriAPPLE (GLenum, GLint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, GLvoid *pointer); +typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, GLvoid *pointer); +typedef void (APIENTRYP PFNGLVERTEXARRAYPARAMETERIAPPLEPROC) (GLenum pname, GLint param); +#endif + +#ifndef GL_APPLE_ycbcr_422 +#define GL_APPLE_ycbcr_422 1 +#endif + +#ifndef GL_S3_s3tc +#define GL_S3_s3tc 1 +#endif + +#ifndef GL_ATI_draw_buffers +#define GL_ATI_draw_buffers 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawBuffersATI (GLsizei, const GLenum *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWBUFFERSATIPROC) (GLsizei n, const GLenum *bufs); +#endif + +#ifndef GL_ATI_pixel_format_float +#define GL_ATI_pixel_format_float 1 +/* This is really a WGL extension, but defines some associated GL enums. + * ATI does not export "GL_ATI_pixel_format_float" in the GL_EXTENSIONS string. + */ +#endif + +#ifndef GL_ATI_texture_env_combine3 +#define GL_ATI_texture_env_combine3 1 +#endif + +#ifndef GL_ATI_texture_float +#define GL_ATI_texture_float 1 +#endif + +#ifndef GL_NV_float_buffer +#define GL_NV_float_buffer 1 +#endif + +#ifndef GL_NV_fragment_program +#define GL_NV_fragment_program 1 +/* Some NV_fragment_program entry points are shared with ARB_vertex_program. */ +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramNamedParameter4fNV (GLuint, GLsizei, const GLubyte *, GLfloat, GLfloat, GLfloat, GLfloat); +GLAPI void APIENTRY glProgramNamedParameter4dNV (GLuint, GLsizei, const GLubyte *, GLdouble, GLdouble, GLdouble, GLdouble); +GLAPI void APIENTRY glProgramNamedParameter4fvNV (GLuint, GLsizei, const GLubyte *, const GLfloat *); +GLAPI void APIENTRY glProgramNamedParameter4dvNV (GLuint, GLsizei, const GLubyte *, const GLdouble *); +GLAPI void APIENTRY glGetProgramNamedParameterfvNV (GLuint, GLsizei, const GLubyte *, GLfloat *); +GLAPI void APIENTRY glGetProgramNamedParameterdvNV (GLuint, GLsizei, const GLubyte *, GLdouble *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); +typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); +#endif + +#ifndef GL_NV_half_float +#define GL_NV_half_float 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertex2hNV (GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glVertex2hvNV (const GLhalfNV *); +GLAPI void APIENTRY glVertex3hNV (GLhalfNV, GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glVertex3hvNV (const GLhalfNV *); +GLAPI void APIENTRY glVertex4hNV (GLhalfNV, GLhalfNV, GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glVertex4hvNV (const GLhalfNV *); +GLAPI void APIENTRY glNormal3hNV (GLhalfNV, GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glNormal3hvNV (const GLhalfNV *); +GLAPI void APIENTRY glColor3hNV (GLhalfNV, GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glColor3hvNV (const GLhalfNV *); +GLAPI void APIENTRY glColor4hNV (GLhalfNV, GLhalfNV, GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glColor4hvNV (const GLhalfNV *); +GLAPI void APIENTRY glTexCoord1hNV (GLhalfNV); +GLAPI void APIENTRY glTexCoord1hvNV (const GLhalfNV *); +GLAPI void APIENTRY glTexCoord2hNV (GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glTexCoord2hvNV (const GLhalfNV *); +GLAPI void APIENTRY glTexCoord3hNV (GLhalfNV, GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glTexCoord3hvNV (const GLhalfNV *); +GLAPI void APIENTRY glTexCoord4hNV (GLhalfNV, GLhalfNV, GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glTexCoord4hvNV (const GLhalfNV *); +GLAPI void APIENTRY glMultiTexCoord1hNV (GLenum, GLhalfNV); +GLAPI void APIENTRY glMultiTexCoord1hvNV (GLenum, const GLhalfNV *); +GLAPI void APIENTRY glMultiTexCoord2hNV (GLenum, GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glMultiTexCoord2hvNV (GLenum, const GLhalfNV *); +GLAPI void APIENTRY glMultiTexCoord3hNV (GLenum, GLhalfNV, GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glMultiTexCoord3hvNV (GLenum, const GLhalfNV *); +GLAPI void APIENTRY glMultiTexCoord4hNV (GLenum, GLhalfNV, GLhalfNV, GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glMultiTexCoord4hvNV (GLenum, const GLhalfNV *); +GLAPI void APIENTRY glFogCoordhNV (GLhalfNV); +GLAPI void APIENTRY glFogCoordhvNV (const GLhalfNV *); +GLAPI void APIENTRY glSecondaryColor3hNV (GLhalfNV, GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glSecondaryColor3hvNV (const GLhalfNV *); +GLAPI void APIENTRY glVertexWeighthNV (GLhalfNV); +GLAPI void APIENTRY glVertexWeighthvNV (const GLhalfNV *); +GLAPI void APIENTRY glVertexAttrib1hNV (GLuint, GLhalfNV); +GLAPI void APIENTRY glVertexAttrib1hvNV (GLuint, const GLhalfNV *); +GLAPI void APIENTRY glVertexAttrib2hNV (GLuint, GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glVertexAttrib2hvNV (GLuint, const GLhalfNV *); +GLAPI void APIENTRY glVertexAttrib3hNV (GLuint, GLhalfNV, GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glVertexAttrib3hvNV (GLuint, const GLhalfNV *); +GLAPI void APIENTRY glVertexAttrib4hNV (GLuint, GLhalfNV, GLhalfNV, GLhalfNV, GLhalfNV); +GLAPI void APIENTRY glVertexAttrib4hvNV (GLuint, const GLhalfNV *); +GLAPI void APIENTRY glVertexAttribs1hvNV (GLuint, GLsizei, const GLhalfNV *); +GLAPI void APIENTRY glVertexAttribs2hvNV (GLuint, GLsizei, const GLhalfNV *); +GLAPI void APIENTRY glVertexAttribs3hvNV (GLuint, GLsizei, const GLhalfNV *); +GLAPI void APIENTRY glVertexAttribs4hvNV (GLuint, GLsizei, const GLhalfNV *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEX2HNVPROC) (GLhalfNV x, GLhalfNV y); +typedef void (APIENTRYP PFNGLVERTEX2HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEX3HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z); +typedef void (APIENTRYP PFNGLVERTEX3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEX4HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +typedef void (APIENTRYP PFNGLVERTEX4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLNORMAL3HNVPROC) (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); +typedef void (APIENTRYP PFNGLNORMAL3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +typedef void (APIENTRYP PFNGLCOLOR3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLCOLOR4HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); +typedef void (APIENTRYP PFNGLCOLOR4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD1HNVPROC) (GLhalfNV s); +typedef void (APIENTRYP PFNGLTEXCOORD1HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD2HNVPROC) (GLhalfNV s, GLhalfNV t); +typedef void (APIENTRYP PFNGLTEXCOORD2HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD3HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r); +typedef void (APIENTRYP PFNGLTEXCOORD3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD4HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +typedef void (APIENTRYP PFNGLTEXCOORD4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1HNVPROC) (GLenum target, GLhalfNV s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLFOGCOORDHNVPROC) (GLhalfNV fog); +typedef void (APIENTRYP PFNGLFOGCOORDHVNVPROC) (const GLhalfNV *fog); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTHNVPROC) (GLhalfNV weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTHVNVPROC) (const GLhalfNV *weight); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1HNVPROC) (GLuint index, GLhalfNV x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +#endif + +#ifndef GL_NV_pixel_data_range +#define GL_NV_pixel_data_range 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelDataRangeNV (GLenum, GLsizei, GLvoid *); +GLAPI void APIENTRY glFlushPixelDataRangeNV (GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC) (GLenum target, GLsizei length, GLvoid *pointer); +typedef void (APIENTRYP PFNGLFLUSHPIXELDATARANGENVPROC) (GLenum target); +#endif + +#ifndef GL_NV_primitive_restart +#define GL_NV_primitive_restart 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPrimitiveRestartNV (void); +GLAPI void APIENTRY glPrimitiveRestartIndexNV (GLuint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTNVPROC) (void); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXNVPROC) (GLuint index); +#endif + +#ifndef GL_NV_texture_expand_normal +#define GL_NV_texture_expand_normal 1 +#endif + +#ifndef GL_NV_vertex_program2 +#define GL_NV_vertex_program2 1 +#endif + +#ifndef GL_ATI_map_object_buffer +#define GL_ATI_map_object_buffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLvoid* APIENTRY glMapObjectBufferATI (GLuint); +GLAPI void APIENTRY glUnmapObjectBufferATI (GLuint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLvoid* (APIENTRYP PFNGLMAPOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLUNMAPOBJECTBUFFERATIPROC) (GLuint buffer); +#endif + +#ifndef GL_ATI_separate_stencil +#define GL_ATI_separate_stencil 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilOpSeparateATI (GLenum, GLenum, GLenum, GLenum); +GLAPI void APIENTRY glStencilFuncSeparateATI (GLenum, GLenum, GLint, GLuint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEATIPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEATIPROC) (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); +#endif + +#ifndef GL_ATI_vertex_attrib_array_object +#define GL_ATI_vertex_attrib_array_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribArrayObjectATI (GLuint, GLint, GLenum, GLboolean, GLsizei, GLuint, GLuint); +GLAPI void APIENTRY glGetVertexAttribArrayObjectfvATI (GLuint, GLenum, GLfloat *); +GLAPI void APIENTRY glGetVertexAttribArrayObjectivATI (GLuint, GLenum, GLint *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXATTRIBARRAYOBJECTATIPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC) (GLuint index, GLenum pname, GLint *params); +#endif + +#ifndef GL_OES_read_format +#define GL_OES_read_format 1 +#endif + +#ifndef GL_EXT_depth_bounds_test +#define GL_EXT_depth_bounds_test 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthBoundsEXT (GLclampd, GLclampd); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDEPTHBOUNDSEXTPROC) (GLclampd zmin, GLclampd zmax); +#endif + +#ifndef GL_EXT_texture_mirror_clamp +#define GL_EXT_texture_mirror_clamp 1 +#endif + +#ifndef GL_EXT_blend_equation_separate +#define GL_EXT_blend_equation_separate 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparateEXT (GLenum, GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEEXTPROC) (GLenum modeRGB, GLenum modeAlpha); +#endif + +#ifndef GL_MESA_pack_invert +#define GL_MESA_pack_invert 1 +#endif + +#ifndef GL_MESA_ycbcr_texture +#define GL_MESA_ycbcr_texture 1 +#endif + +#ifndef GL_EXT_pixel_buffer_object +#define GL_EXT_pixel_buffer_object 1 +#endif + +#ifndef GL_NV_fragment_program_option +#define GL_NV_fragment_program_option 1 +#endif + +#ifndef GL_NV_fragment_program2 +#define GL_NV_fragment_program2 1 +#endif + +#ifndef GL_NV_vertex_program2_option +#define GL_NV_vertex_program2_option 1 +#endif + +#ifndef GL_NV_vertex_program3 +#define GL_NV_vertex_program3 1 +#endif + +#ifndef GL_EXT_framebuffer_object +#define GL_EXT_framebuffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glIsRenderbufferEXT (GLuint); +GLAPI void APIENTRY glBindRenderbufferEXT (GLenum, GLuint); +GLAPI void APIENTRY glDeleteRenderbuffersEXT (GLsizei, const GLuint *); +GLAPI void APIENTRY glGenRenderbuffersEXT (GLsizei, GLuint *); +GLAPI void APIENTRY glRenderbufferStorageEXT (GLenum, GLenum, GLsizei, GLsizei); +GLAPI void APIENTRY glGetRenderbufferParameterivEXT (GLenum, GLenum, GLint *); +GLAPI GLboolean APIENTRY glIsFramebufferEXT (GLuint); +GLAPI void APIENTRY glBindFramebufferEXT (GLenum, GLuint); +GLAPI void APIENTRY glDeleteFramebuffersEXT (GLsizei, const GLuint *); +GLAPI void APIENTRY glGenFramebuffersEXT (GLsizei, GLuint *); +GLAPI GLenum APIENTRY glCheckFramebufferStatusEXT (GLenum); +GLAPI void APIENTRY glFramebufferTexture1DEXT (GLenum, GLenum, GLenum, GLuint, GLint); +GLAPI void APIENTRY glFramebufferTexture2DEXT (GLenum, GLenum, GLenum, GLuint, GLint); +GLAPI void APIENTRY glFramebufferTexture3DEXT (GLenum, GLenum, GLenum, GLuint, GLint, GLint); +GLAPI void APIENTRY glFramebufferRenderbufferEXT (GLenum, GLenum, GLenum, GLuint); +GLAPI void APIENTRY glGetFramebufferAttachmentParameterivEXT (GLenum, GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGenerateMipmapEXT (GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFEREXTPROC) (GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC) (GLenum target); +#endif + +#ifndef GL_GREMEDY_string_marker +#define GL_GREMEDY_string_marker 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStringMarkerGREMEDY (GLsizei, const GLvoid *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSTRINGMARKERGREMEDYPROC) (GLsizei len, const GLvoid *string); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif +#endif /* NO_SDL_GLEXT */ diff --git a/reaction/engine/code/SDL12/include/SDL_platform.h b/reaction/engine/code/SDL12/include/SDL_platform.h new file mode 100644 index 00000000..80274bc4 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_platform.h @@ -0,0 +1,104 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* Try to get a standard set of platform defines */ + +#ifndef _SDL_platform_h +#define _SDL_platform_h + +#if defined(_AIX) +#undef __AIX__ +#define __AIX__ 1 +#endif +#if defined(AMIGA) || defined(__AMIGA) || defined(__amigados__) +#undef __AMIGA__ +#define __AMIGA__ 1 +#endif +#if defined(__BEOS__) +#undef __BEOS__ +#define __BEOS__ 1 +#endif +#if defined(bsdi) || defined(__bsdi) || defined(__bsdi__) +#undef __BSDI__ +#define __BSDI__ 1 +#endif +#if defined(_arch_dreamcast) +#undef __DREAMCAST__ +#define __DREAMCAST__ 1 +#endif +#if defined(__FreeBSD__) || defined(__DragonFly__) +#undef __FREEBSD__ +#define __FREEBSD__ 1 +#endif +#if defined(hpux) || defined(__hpux) || defined(__hpux__) +#undef __HPUX__ +#define __HPUX__ 1 +#endif +#if defined(sgi) || defined(__sgi) || defined(__sgi__) || defined(_SGI_SOURCE) +#undef __IRIX__ +#define __IRIX__ 1 +#endif +#if defined(linux) || defined(__linux) || defined(__linux__) +#undef __LINUX__ +#define __LINUX__ 1 +#endif +#if defined(__APPLE__) +#undef __MACOSX__ +#define __MACOSX__ 1 +#elif defined(macintosh) +#undef __MACOS__ +#define __MACOS__ 1 +#endif +#if defined(__NetBSD__) +#undef __NETBSD__ +#define __NETBSD__ 1 +#endif +#if defined(__OpenBSD__) +#undef __OPENBSD__ +#define __OPENBSD__ 1 +#endif +#if defined(__OS2__) +#undef __OS2__ +#define __OS2__ 1 +#endif +#if defined(osf) || defined(__osf) || defined(__osf__) || defined(_OSF_SOURCE) +#undef __OSF__ +#define __OSF__ 1 +#endif +#if defined(__QNXNTO__) +#undef __QNXNTO__ +#define __QNXNTO__ 1 +#endif +#if defined(riscos) || defined(__riscos) || defined(__riscos__) +#undef __RISCOS__ +#define __RISCOS__ 1 +#endif +#if defined(__SVR4) +#undef __SOLARIS__ +#define __SOLARIS__ 1 +#endif +#if defined(WIN32) || defined(_WIN32) +#undef __WIN32__ +#define __WIN32__ 1 +#endif + +#endif /* _SDL_platform_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_quit.h b/reaction/engine/code/SDL12/include/SDL_quit.h new file mode 100644 index 00000000..fcf40fbd --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_quit.h @@ -0,0 +1,50 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* Include file for SDL quit event handling */ + +#ifndef _SDL_quit_h +#define _SDL_quit_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +/* + An SDL_QUITEVENT is generated when the user tries to close the application + window. If it is ignored or filtered out, the window will remain open. + If it is not ignored or filtered, it is queued normally and the window + is allowed to close. When the window is closed, screen updates will + complete, but have no effect. + + SDL_Init() installs signal handlers for SIGINT (keyboard interrupt) + and SIGTERM (system termination request), if handlers do not already + exist, that generate SDL_QUITEVENT events as well. There is no way + to determine the cause of an SDL_QUITEVENT, but setting a signal + handler in your application will override the default generation of + quit events for that signal. +*/ + +/* There are no functions directly affecting the quit event */ +#define SDL_QuitRequested() \ + (SDL_PumpEvents(), SDL_PeepEvents(NULL,0,SDL_PEEKEVENT,SDL_QUITMASK)) + +#endif /* _SDL_quit_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_rwops.h b/reaction/engine/code/SDL12/include/SDL_rwops.h new file mode 100644 index 00000000..d7e01d8f --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_rwops.h @@ -0,0 +1,139 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* This file provides a general interface for SDL to read and write + data sources. It can easily be extended to files, memory, etc. +*/ + +#ifndef _SDL_rwops_h +#define _SDL_rwops_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* This is the read/write operation structure -- very basic */ + +typedef struct SDL_RWops { + /* Seek to 'offset' relative to whence, one of stdio's whence values: + SEEK_SET, SEEK_CUR, SEEK_END + Returns the final offset in the data source. + */ + int (SDLCALL *seek)(struct SDL_RWops *context, int offset, int whence); + + /* Read up to 'num' objects each of size 'objsize' from the data + source to the area pointed at by 'ptr'. + Returns the number of objects read, or -1 if the read failed. + */ + int (SDLCALL *read)(struct SDL_RWops *context, void *ptr, int size, int maxnum); + + /* Write exactly 'num' objects each of size 'objsize' from the area + pointed at by 'ptr' to data source. + Returns 'num', or -1 if the write failed. + */ + int (SDLCALL *write)(struct SDL_RWops *context, const void *ptr, int size, int num); + + /* Close and free an allocated SDL_FSops structure */ + int (SDLCALL *close)(struct SDL_RWops *context); + + Uint32 type; + union { +#ifdef __WIN32__ + struct { + int append; + void* h; + } win32io; +#endif +#ifdef HAVE_STDIO_H + struct { + int autoclose; + FILE *fp; + } stdio; +#endif + struct { + Uint8 *base; + Uint8 *here; + Uint8 *stop; + } mem; + struct { + void *data1; + } unknown; + } hidden; + +} SDL_RWops; + + +/* Functions to create SDL_RWops structures from various data sources */ + +extern DECLSPEC SDL_RWops * SDLCALL SDL_RWFromFile(const char *file, const char *mode); + +#ifdef HAVE_STDIO_H +extern DECLSPEC SDL_RWops * SDLCALL SDL_RWFromFP(FILE *fp, int autoclose); +#endif + +extern DECLSPEC SDL_RWops * SDLCALL SDL_RWFromMem(void *mem, int size); +extern DECLSPEC SDL_RWops * SDLCALL SDL_RWFromConstMem(const void *mem, int size); + +extern DECLSPEC SDL_RWops * SDLCALL SDL_AllocRW(void); +extern DECLSPEC void SDLCALL SDL_FreeRW(SDL_RWops *area); + +#define RW_SEEK_SET 0 /* Seek from the beginning of data */ +#define RW_SEEK_CUR 1 /* Seek relative to current read point */ +#define RW_SEEK_END 2 /* Seek relative to the end of data */ + +/* Macros to easily read and write from an SDL_RWops structure */ +#define SDL_RWseek(ctx, offset, whence) (ctx)->seek(ctx, offset, whence) +#define SDL_RWtell(ctx) (ctx)->seek(ctx, 0, RW_SEEK_CUR) +#define SDL_RWread(ctx, ptr, size, n) (ctx)->read(ctx, ptr, size, n) +#define SDL_RWwrite(ctx, ptr, size, n) (ctx)->write(ctx, ptr, size, n) +#define SDL_RWclose(ctx) (ctx)->close(ctx) + + +/* Read an item of the specified endianness and return in native format */ +extern DECLSPEC Uint16 SDLCALL SDL_ReadLE16(SDL_RWops *src); +extern DECLSPEC Uint16 SDLCALL SDL_ReadBE16(SDL_RWops *src); +extern DECLSPEC Uint32 SDLCALL SDL_ReadLE32(SDL_RWops *src); +extern DECLSPEC Uint32 SDLCALL SDL_ReadBE32(SDL_RWops *src); +extern DECLSPEC Uint64 SDLCALL SDL_ReadLE64(SDL_RWops *src); +extern DECLSPEC Uint64 SDLCALL SDL_ReadBE64(SDL_RWops *src); + +/* Write an item of native format to the specified endianness */ +extern DECLSPEC int SDLCALL SDL_WriteLE16(SDL_RWops *dst, Uint16 value); +extern DECLSPEC int SDLCALL SDL_WriteBE16(SDL_RWops *dst, Uint16 value); +extern DECLSPEC int SDLCALL SDL_WriteLE32(SDL_RWops *dst, Uint32 value); +extern DECLSPEC int SDLCALL SDL_WriteBE32(SDL_RWops *dst, Uint32 value); +extern DECLSPEC int SDLCALL SDL_WriteLE64(SDL_RWops *dst, Uint64 value); +extern DECLSPEC int SDLCALL SDL_WriteBE64(SDL_RWops *dst, Uint64 value); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_rwops_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_stdinc.h b/reaction/engine/code/SDL12/include/SDL_stdinc.h new file mode 100644 index 00000000..62535629 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_stdinc.h @@ -0,0 +1,586 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* This is a general header that includes C language support */ + +#ifndef _SDL_stdinc_h +#define _SDL_stdinc_h + +#include "SDL_config.h" + + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#if defined(STDC_HEADERS) +# include +# include +# include +#else +# if defined(HAVE_STDLIB_H) +# include +# elif defined(HAVE_MALLOC_H) +# include +# endif +# if defined(HAVE_STDDEF_H) +# include +# endif +# if defined(HAVE_STDARG_H) +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H) +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#if defined(HAVE_INTTYPES_H) +# include +#elif defined(HAVE_STDINT_H) +# include +#endif +#ifdef HAVE_CTYPE_H +# include +#endif +#ifdef HAVE_ICONV_H +# include +#endif + +/* The number of elements in an array */ +#define SDL_arraysize(array) (sizeof(array)/sizeof(array[0])) +#define SDL_TABLESIZE(table) SDL_arraysize(table) + +/* Basic data types */ +typedef enum SDL_bool { + SDL_FALSE = 0, + SDL_TRUE = 1 +} SDL_bool; + +typedef int8_t Sint8; +typedef uint8_t Uint8; +typedef int16_t Sint16; +typedef uint16_t Uint16; +typedef int32_t Sint32; +typedef uint32_t Uint32; + +#ifdef SDL_HAS_64BIT_TYPE +typedef int64_t Sint64; +typedef uint64_t Uint64; +#else +/* This is really just a hack to prevent the compiler from complaining */ +typedef struct { + Uint32 hi; + Uint32 lo; +} Uint64, Sint64; +#endif + +/* Make sure the types really have the right sizes */ +#define SDL_COMPILE_TIME_ASSERT(name, x) \ + typedef int SDL_dummy_ ## name[(x) * 2 - 1] + +SDL_COMPILE_TIME_ASSERT(uint8, sizeof(Uint8) == 1); +SDL_COMPILE_TIME_ASSERT(sint8, sizeof(Sint8) == 1); +SDL_COMPILE_TIME_ASSERT(uint16, sizeof(Uint16) == 2); +SDL_COMPILE_TIME_ASSERT(sint16, sizeof(Sint16) == 2); +SDL_COMPILE_TIME_ASSERT(uint32, sizeof(Uint32) == 4); +SDL_COMPILE_TIME_ASSERT(sint32, sizeof(Sint32) == 4); +SDL_COMPILE_TIME_ASSERT(uint64, sizeof(Uint64) == 8); +SDL_COMPILE_TIME_ASSERT(sint64, sizeof(Sint64) == 8); + +/* Check to make sure enums are the size of ints, for structure packing. + For both Watcom C/C++ and Borland C/C++ the compiler option that makes + enums having the size of an int must be enabled. + This is "-b" for Borland C/C++ and "-ei" for Watcom C/C++ (v11). +*/ +/* Enable enums always int in CodeWarrior (for MPW use "-enum int") */ +#ifdef __MWERKS__ +#pragma enumsalwaysint on +#endif + +typedef enum { + DUMMY_ENUM_VALUE +} SDL_DUMMY_ENUM; + +SDL_COMPILE_TIME_ASSERT(enum, sizeof(SDL_DUMMY_ENUM) == sizeof(int)); + + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_MALLOC +#define SDL_malloc malloc +#else +extern DECLSPEC void * SDLCALL SDL_malloc(size_t size); +#endif + +#ifdef HAVE_CALLOC +#define SDL_calloc calloc +#else +extern DECLSPEC void * SDLCALL SDL_calloc(size_t nmemb, size_t size); +#endif + +#ifdef HAVE_REALLOC +#define SDL_realloc realloc +#else +extern DECLSPEC void * SDLCALL SDL_realloc(void *mem, size_t size); +#endif + +#ifdef HAVE_FREE +#define SDL_free free +#else +extern DECLSPEC void SDLCALL SDL_free(void *mem); +#endif + +#if defined(HAVE_ALLOCA) && !defined(alloca) +# if defined(HAVE_ALLOCA_H) +# include +# elif defined(__GNUC__) +# define alloca __builtin_alloca +# elif defined(_MSC_VER) +# include +# define alloca _alloca +# elif defined(__WATCOMC__) +# include +# elif defined(__DMC__) +# include +# elif defined(__AIX__) + #pragma alloca +# elif defined(__MRC__) + void *alloca (unsigned); +# else + char *alloca (); +# endif +#endif +#ifdef HAVE_ALLOCA +#define SDL_stack_alloc(type, count) (type*)alloca(sizeof(type)*count) +#define SDL_stack_free(data) +#else +#define SDL_stack_alloc(type, count) (type*)SDL_malloc(sizeof(type)*count) +#define SDL_stack_free(data) SDL_free(data) +#endif + +#ifdef HAVE_GETENV +#define SDL_getenv getenv +#else +extern DECLSPEC char * SDLCALL SDL_getenv(const char *name); +#endif + +#ifdef HAVE_PUTENV +#define SDL_putenv putenv +#else +extern DECLSPEC int SDLCALL SDL_putenv(const char *variable); +#endif + +#ifdef HAVE_QSORT +#define SDL_qsort qsort +#else +extern DECLSPEC void SDLCALL SDL_qsort(void *base, size_t nmemb, size_t size, + int (*compare)(const void *, const void *)); +#endif + +#ifdef HAVE_ABS +#define SDL_abs abs +#else +#define SDL_abs(X) ((X) < 0 ? -(X) : (X)) +#endif + +#define SDL_min(x, y) (((x) < (y)) ? (x) : (y)) +#define SDL_max(x, y) (((x) > (y)) ? (x) : (y)) + +#ifdef HAVE_CTYPE_H +#define SDL_isdigit(X) isdigit(X) +#define SDL_isspace(X) isspace(X) +#define SDL_toupper(X) toupper(X) +#define SDL_tolower(X) tolower(X) +#else +#define SDL_isdigit(X) (((X) >= '0') && ((X) <= '9')) +#define SDL_isspace(X) (((X) == ' ') || ((X) == '\t') || ((X) == '\r') || ((X) == '\n')) +#define SDL_toupper(X) (((X) >= 'a') && ((X) <= 'z') ? ('A'+((X)-'a')) : (X)) +#define SDL_tolower(X) (((X) >= 'A') && ((X) <= 'Z') ? ('a'+((X)-'A')) : (X)) +#endif + +#ifdef HAVE_MEMSET +#define SDL_memset memset +#else +extern DECLSPEC void * SDLCALL SDL_memset(void *dst, int c, size_t len); +#endif + +#if defined(__GNUC__) && defined(i386) +#define SDL_memset4(dst, val, len) \ +do { \ + int u0, u1, u2; \ + __asm__ __volatile__ ( \ + "cld\n\t" \ + "rep ; stosl\n\t" \ + : "=&D" (u0), "=&a" (u1), "=&c" (u2) \ + : "0" (dst), "1" (val), "2" ((Uint32)(len)) \ + : "memory" ); \ +} while(0) +#endif +#ifndef SDL_memset4 +#define SDL_memset4(dst, val, len) \ +do { \ + unsigned _count = (len); \ + unsigned _n = (_count + 3) / 4; \ + Uint32 *_p = (Uint32 *)(dst); \ + Uint32 _val = (val); \ + switch (_count % 4) { \ + case 0: do { *_p++ = _val; \ + case 3: *_p++ = _val; \ + case 2: *_p++ = _val; \ + case 1: *_p++ = _val; \ + } while ( --_n ); \ + } \ +} while(0) +#endif + +#if defined(__GNUC__) && defined(i386) +#define SDL_memcpy(dst, src, len) \ +do { \ + int u0, u1, u2; \ + __asm__ __volatile__ ( \ + "cld\n\t" \ + "rep ; movsl\n\t" \ + "testb $2,%b4\n\t" \ + "je 1f\n\t" \ + "movsw\n" \ + "1:\ttestb $1,%b4\n\t" \ + "je 2f\n\t" \ + "movsb\n" \ + "2:" \ + : "=&c" (u0), "=&D" (u1), "=&S" (u2) \ + : "0" ((unsigned)(len)/4), "q" (len), "1" (dst),"2" (src) \ + : "memory" ); \ +} while(0) +#endif +#ifndef SDL_memcpy +#ifdef HAVE_MEMCPY +#define SDL_memcpy memcpy +#elif defined(HAVE_BCOPY) +#define SDL_memcpy(d, s, n) bcopy((s), (d), (n)) +#else +extern DECLSPEC void * SDLCALL SDL_memcpy(void *dst, const void *src, size_t len); +#endif +#endif + +#if defined(__GNUC__) && defined(i386) +#define SDL_memcpy4(dst, src, len) \ +do { \ + int ecx, edi, esi; \ + __asm__ __volatile__ ( \ + "cld\n\t" \ + "rep ; movsl" \ + : "=&c" (ecx), "=&D" (edi), "=&S" (esi) \ + : "0" ((unsigned)(len)), "1" (dst), "2" (src) \ + : "memory" ); \ +} while(0) +#endif +#ifndef SDL_memcpy4 +#define SDL_memcpy4(dst, src, len) SDL_memcpy(dst, src, (len) << 2) +#endif + +#if defined(__GNUC__) && defined(i386) +#define SDL_revcpy(dst, src, len) \ +do { \ + int u0, u1, u2; \ + char *dstp = (char *)(dst); \ + char *srcp = (char *)(src); \ + int n = (len); \ + if ( n >= 4 ) { \ + __asm__ __volatile__ ( \ + "std\n\t" \ + "rep ; movsl\n\t" \ + : "=&c" (u0), "=&D" (u1), "=&S" (u2) \ + : "0" (n >> 2), \ + "1" (dstp+(n-4)), "2" (srcp+(n-4)) \ + : "memory" ); \ + } \ + switch (n & 3) { \ + case 3: dstp[2] = srcp[2]; \ + case 2: dstp[1] = srcp[1]; \ + case 1: dstp[0] = srcp[0]; \ + break; \ + default: \ + break; \ + } \ +} while(0) +#endif +#ifndef SDL_revcpy +extern DECLSPEC void * SDLCALL SDL_revcpy(void *dst, const void *src, size_t len); +#endif + +#ifdef HAVE_MEMMOVE +#define SDL_memmove memmove +#elif defined(HAVE_BCOPY) +#define SDL_memmove(d, s, n) bcopy((s), (d), (n)) +#else +#define SDL_memmove(dst, src, len) \ +do { \ + if ( dst < src ) { \ + SDL_memcpy(dst, src, len); \ + } else { \ + SDL_revcpy(dst, src, len); \ + } \ +} while(0) +#endif + +#ifdef HAVE_MEMCMP +#define SDL_memcmp memcmp +#else +extern DECLSPEC int SDLCALL SDL_memcmp(const void *s1, const void *s2, size_t len); +#endif + +#ifdef HAVE_STRLEN +#define SDL_strlen strlen +#else +extern DECLSPEC size_t SDLCALL SDL_strlen(const char *string); +#endif + +#ifdef HAVE_STRLCPY +#define SDL_strlcpy strlcpy +#else +extern DECLSPEC size_t SDLCALL SDL_strlcpy(char *dst, const char *src, size_t maxlen); +#endif + +#ifdef HAVE_STRLCAT +#define SDL_strlcat strlcat +#else +extern DECLSPEC size_t SDLCALL SDL_strlcat(char *dst, const char *src, size_t maxlen); +#endif + +#ifdef HAVE_STRDUP +#define SDL_strdup strdup +#else +extern DECLSPEC char * SDLCALL SDL_strdup(const char *string); +#endif + +#ifdef HAVE__STRREV +#define SDL_strrev _strrev +#else +extern DECLSPEC char * SDLCALL SDL_strrev(char *string); +#endif + +#ifdef HAVE__STRUPR +#define SDL_strupr _strupr +#else +extern DECLSPEC char * SDLCALL SDL_strupr(char *string); +#endif + +#ifdef HAVE__STRLWR +#define SDL_strlwr _strlwr +#else +extern DECLSPEC char * SDLCALL SDL_strlwr(char *string); +#endif + +#ifdef HAVE_STRCHR +#define SDL_strchr strchr +#elif defined(HAVE_INDEX) +#define SDL_strchr index +#else +extern DECLSPEC char * SDLCALL SDL_strchr(const char *string, int c); +#endif + +#ifdef HAVE_STRRCHR +#define SDL_strrchr strrchr +#elif defined(HAVE_RINDEX) +#define SDL_strrchr rindex +#else +extern DECLSPEC char * SDLCALL SDL_strrchr(const char *string, int c); +#endif + +#ifdef HAVE_STRSTR +#define SDL_strstr strstr +#else +extern DECLSPEC char * SDLCALL SDL_strstr(const char *haystack, const char *needle); +#endif + +#ifdef HAVE_ITOA +#define SDL_itoa itoa +#else +#define SDL_itoa(value, string, radix) SDL_ltoa((long)value, string, radix) +#endif + +#ifdef HAVE__LTOA +#define SDL_ltoa _ltoa +#else +extern DECLSPEC char * SDLCALL SDL_ltoa(long value, char *string, int radix); +#endif + +#ifdef HAVE__UITOA +#define SDL_uitoa _uitoa +#else +#define SDL_uitoa(value, string, radix) SDL_ultoa((long)value, string, radix) +#endif + +#ifdef HAVE__ULTOA +#define SDL_ultoa _ultoa +#else +extern DECLSPEC char * SDLCALL SDL_ultoa(unsigned long value, char *string, int radix); +#endif + +#ifdef HAVE_STRTOL +#define SDL_strtol strtol +#else +extern DECLSPEC long SDLCALL SDL_strtol(const char *string, char **endp, int base); +#endif + +#ifdef HAVE_STRTOUL +#define SDL_strtoul strtoul +#else +extern DECLSPEC unsigned long SDLCALL SDL_strtoul(const char *string, char **endp, int base); +#endif + +#ifdef SDL_HAS_64BIT_TYPE + +#ifdef HAVE__I64TOA +#define SDL_lltoa _i64toa +#else +extern DECLSPEC char* SDLCALL SDL_lltoa(Sint64 value, char *string, int radix); +#endif + +#ifdef HAVE__UI64TOA +#define SDL_ulltoa _ui64toa +#else +extern DECLSPEC char* SDLCALL SDL_ulltoa(Uint64 value, char *string, int radix); +#endif + +#ifdef HAVE_STRTOLL +#define SDL_strtoll strtoll +#else +extern DECLSPEC Sint64 SDLCALL SDL_strtoll(const char *string, char **endp, int base); +#endif + +#ifdef HAVE_STRTOULL +#define SDL_strtoull strtoull +#else +extern DECLSPEC Uint64 SDLCALL SDL_strtoull(const char *string, char **endp, int base); +#endif + +#endif /* SDL_HAS_64BIT_TYPE */ + +#ifdef HAVE_STRTOD +#define SDL_strtod strtod +#else +extern DECLSPEC double SDLCALL SDL_strtod(const char *string, char **endp); +#endif + +#ifdef HAVE_ATOI +#define SDL_atoi atoi +#else +#define SDL_atoi(X) SDL_strtol(X, NULL, 0) +#endif + +#ifdef HAVE_ATOF +#define SDL_atof atof +#else +#define SDL_atof(X) SDL_strtod(X, NULL) +#endif + +#ifdef HAVE_STRCMP +#define SDL_strcmp strcmp +#else +extern DECLSPEC int SDLCALL SDL_strcmp(const char *str1, const char *str2); +#endif + +#ifdef HAVE_STRNCMP +#define SDL_strncmp strncmp +#else +extern DECLSPEC int SDLCALL SDL_strncmp(const char *str1, const char *str2, size_t maxlen); +#endif + +#ifdef HAVE_STRCASECMP +#define SDL_strcasecmp strcasecmp +#elif defined(HAVE__STRICMP) +#define SDL_strcasecmp _stricmp +#else +extern DECLSPEC int SDLCALL SDL_strcasecmp(const char *str1, const char *str2); +#endif + +#ifdef HAVE_STRNCASECMP +#define SDL_strncasecmp strncasecmp +#elif defined(HAVE__STRNICMP) +#define SDL_strncasecmp _strnicmp +#else +extern DECLSPEC int SDLCALL SDL_strncasecmp(const char *str1, const char *str2, size_t maxlen); +#endif + +#ifdef HAVE_SSCANF +#define SDL_sscanf sscanf +#else +extern DECLSPEC int SDLCALL SDL_sscanf(const char *text, const char *fmt, ...); +#endif + +#ifdef HAVE_SNPRINTF +#define SDL_snprintf snprintf +#else +extern DECLSPEC int SDLCALL SDL_snprintf(char *text, size_t maxlen, const char *fmt, ...); +#endif + +#ifdef HAVE_VSNPRINTF +#define SDL_vsnprintf vsnprintf +#else +extern DECLSPEC int SDLCALL SDL_vsnprintf(char *text, size_t maxlen, const char *fmt, va_list ap); +#endif + +/* The SDL implementation of iconv() returns these error codes */ +#define SDL_ICONV_ERROR (size_t)-1 +#define SDL_ICONV_E2BIG (size_t)-2 +#define SDL_ICONV_EILSEQ (size_t)-3 +#define SDL_ICONV_EINVAL (size_t)-4 + +#ifdef HAVE_ICONV +#define SDL_iconv_t iconv_t +#define SDL_iconv_open iconv_open +#define SDL_iconv_close iconv_close +extern DECLSPEC size_t SDLCALL SDL_iconv(SDL_iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); +#else +typedef struct _SDL_iconv_t *SDL_iconv_t; +extern DECLSPEC SDL_iconv_t SDLCALL SDL_iconv_open(const char *tocode, const char *fromcode); +extern DECLSPEC int SDLCALL SDL_iconv_close(SDL_iconv_t cd); +extern DECLSPEC size_t SDLCALL SDL_iconv(SDL_iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); +#endif +/* This function converts a string between encodings in one pass, returning a + string that must be freed with SDL_free() or NULL on error. +*/ +extern DECLSPEC char * SDLCALL SDL_iconv_string(const char *tocode, const char *fromcode, char *inbuf, size_t inbytesleft); +#define SDL_iconv_utf8_ascii(S) SDL_iconv_string("ASCII", "UTF-8", S, SDL_strlen(S)+1) +#define SDL_iconv_utf8_latin1(S) SDL_iconv_string("LATIN1", "UTF-8", S, SDL_strlen(S)+1) +#define SDL_iconv_utf8_ucs2(S) (Uint16 *)SDL_iconv_string("UCS-2", "UTF-8", S, SDL_strlen(S)+1) +#define SDL_iconv_utf8_ucs4(S) (Uint32 *)SDL_iconv_string("UCS-4", "UTF-8", S, SDL_strlen(S)+1) + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_stdinc_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_syswm.h b/reaction/engine/code/SDL12/include/SDL_syswm.h new file mode 100644 index 00000000..e7f35139 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_syswm.h @@ -0,0 +1,210 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* Include file for SDL custom system window manager hooks */ + +#ifndef _SDL_syswm_h +#define _SDL_syswm_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_version.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Your application has access to a special type of event 'SDL_SYSWMEVENT', + which contains window-manager specific information and arrives whenever + an unhandled window event occurs. This event is ignored by default, but + you can enable it with SDL_EventState() +*/ +#ifdef SDL_PROTOTYPES_ONLY +struct SDL_SysWMinfo; +typedef struct SDL_SysWMinfo SDL_SysWMinfo; +#else + +/* This is the structure for custom window manager events */ +#if defined(SDL_VIDEO_DRIVER_X11) +#if defined(__APPLE__) && defined(__MACH__) +/* conflicts with Quickdraw.h */ +#define Cursor X11Cursor +#endif + +#include +#include + +#if defined(__APPLE__) && defined(__MACH__) +/* matches the re-define above */ +#undef Cursor +#endif + +/* These are the various supported subsystems under UNIX */ +typedef enum { + SDL_SYSWM_X11 +} SDL_SYSWM_TYPE; + +/* The UNIX custom event structure */ +struct SDL_SysWMmsg { + SDL_version version; + SDL_SYSWM_TYPE subsystem; + union { + XEvent xevent; + } event; +}; + +/* The UNIX custom window manager information structure. + When this structure is returned, it holds information about which + low level system it is using, and will be one of SDL_SYSWM_TYPE. + */ +typedef struct SDL_SysWMinfo { + SDL_version version; + SDL_SYSWM_TYPE subsystem; + union { + struct { + Display *display; /* The X11 display */ + Window window; /* The X11 display window */ + /* These locking functions should be called around + any X11 functions using the display variable. + They lock the event thread, so should not be + called around event functions or from event filters. + */ + void (*lock_func)(void); + void (*unlock_func)(void); + + /* Introduced in SDL 1.0.2 */ + Window fswindow; /* The X11 fullscreen window */ + Window wmwindow; /* The X11 managed input window */ + } x11; + } info; +} SDL_SysWMinfo; + +#elif defined(SDL_VIDEO_DRIVER_NANOX) +#include + +/* The generic custom event structure */ +struct SDL_SysWMmsg { + SDL_version version; + int data; +}; + +/* The windows custom window manager information structure */ +typedef struct SDL_SysWMinfo { + SDL_version version ; + GR_WINDOW_ID window ; /* The display window */ +} SDL_SysWMinfo; + +#elif defined(SDL_VIDEO_DRIVER_WINDIB) || defined(SDL_VIDEO_DRIVER_DDRAW) || defined(SDL_VIDEO_DRIVER_GAPI) +#define WIN32_LEAN_AND_MEAN +#include + +/* The windows custom event structure */ +struct SDL_SysWMmsg { + SDL_version version; + HWND hwnd; /* The window for the message */ + UINT msg; /* The type of message */ + WPARAM wParam; /* WORD message parameter */ + LPARAM lParam; /* LONG message parameter */ +}; + +/* The windows custom window manager information structure */ +typedef struct SDL_SysWMinfo { + SDL_version version; + HWND window; /* The Win32 display window */ + HGLRC hglrc; /* The OpenGL context, if any */ +} SDL_SysWMinfo; + +#elif defined(SDL_VIDEO_DRIVER_RISCOS) + +/* RISC OS custom event structure */ +struct SDL_SysWMmsg { + SDL_version version; + int eventCode; /* The window for the message */ + int pollBlock[64]; +}; + +/* The RISC OS custom window manager information structure */ +typedef struct SDL_SysWMinfo { + SDL_version version; + int wimpVersion; /* Wimp version running under */ + int taskHandle; /* The RISC OS task handle */ + int window; /* The RISC OS display window */ +} SDL_SysWMinfo; + +#elif defined(SDL_VIDEO_DRIVER_PHOTON) +#include +#include + +/* The QNX custom event structure */ +struct SDL_SysWMmsg { + SDL_version version; + int data; +}; + +/* The QNX custom window manager information structure */ +typedef struct SDL_SysWMinfo { + SDL_version version; + int data; +} SDL_SysWMinfo; + +#else + +/* The generic custom event structure */ +struct SDL_SysWMmsg { + SDL_version version; + int data; +}; + +/* The generic custom window manager information structure */ +typedef struct SDL_SysWMinfo { + SDL_version version; + int data; +} SDL_SysWMinfo; + +#endif /* video driver type */ + +#endif /* SDL_PROTOTYPES_ONLY */ + +/* Function prototypes */ +/* + * This function gives you custom hooks into the window manager information. + * It fills the structure pointed to by 'info' with custom information and + * returns 1 if the function is implemented. If it's not implemented, or + * the version member of the 'info' structure is invalid, it returns 0. + * + * You typically use this function like this: + * SDL_SysWMInfo info; + * SDL_VERSION(&info.version); + * if ( SDL_GetWMInfo(&info) ) { ... } + */ +extern DECLSPEC int SDLCALL SDL_GetWMInfo(SDL_SysWMinfo *info); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_syswm_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_thread.h b/reaction/engine/code/SDL12/include/SDL_thread.h new file mode 100644 index 00000000..e0a35b1a --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_thread.h @@ -0,0 +1,119 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifndef _SDL_thread_h +#define _SDL_thread_h + +/* Header for the SDL thread management routines + + These are independent of the other SDL routines. +*/ + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +/* Thread synchronization primitives */ +#include "SDL_mutex.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* The SDL thread structure, defined in SDL_thread.c */ +struct SDL_Thread; +typedef struct SDL_Thread SDL_Thread; + +/* Create a thread */ +#if (defined(__WIN32__) && !defined(HAVE_LIBC)) || defined(__OS2__) +/* + We compile SDL into a DLL on OS/2. This means, that it's the DLL which + creates a new thread for the calling process with the SDL_CreateThread() + API. There is a problem with this, that only the RTL of the SDL.DLL will + be initialized for those threads, and not the RTL of the calling application! + To solve this, we make a little hack here. + We'll always use the caller's _beginthread() and _endthread() APIs to + start a new thread. This way, if it's the SDL.DLL which uses this API, + then the RTL of SDL.DLL will be used to create the new thread, and if it's + the application, then the RTL of the application will be used. + So, in short: + Always use the _beginthread() and _endthread() of the calling runtime library! +*/ +#define SDL_PASSED_BEGINTHREAD_ENDTHREAD +#ifndef _WIN32_WCE +#include /* This has _beginthread() and _endthread() defined! */ +#endif + +#ifdef __OS2__ +typedef int (*pfnSDL_CurrentBeginThread)(void (*func)(void *), void *, unsigned, void *arg); +typedef void (*pfnSDL_CurrentEndThread)(void); +#elif __GNUC__ +typedef unsigned long (__cdecl *pfnSDL_CurrentBeginThread) (void *, unsigned, + unsigned (__stdcall *func)(void *), void *arg, + unsigned, unsigned *threadID); +typedef void (__cdecl *pfnSDL_CurrentEndThread)(unsigned code); +#else +typedef uintptr_t (__cdecl *pfnSDL_CurrentBeginThread) (void *, unsigned, + unsigned (__stdcall *func)(void *), void *arg, + unsigned, unsigned *threadID); +typedef void (__cdecl *pfnSDL_CurrentEndThread)(unsigned code); +#endif + +extern DECLSPEC SDL_Thread * SDLCALL SDL_CreateThread(int (SDLCALL *fn)(void *), void *data, pfnSDL_CurrentBeginThread pfnBeginThread, pfnSDL_CurrentEndThread pfnEndThread); + +#ifdef __OS2__ +#define SDL_CreateThread(fn, data) SDL_CreateThread(fn, data, _beginthread, _endthread) +#elif defined(_WIN32_WCE) +#define SDL_CreateThread(fn, data) SDL_CreateThread(fn, data, NULL, NULL) +#else +#define SDL_CreateThread(fn, data) SDL_CreateThread(fn, data, _beginthreadex, _endthreadex) +#endif +#else +extern DECLSPEC SDL_Thread * SDLCALL SDL_CreateThread(int (SDLCALL *fn)(void *), void *data); +#endif + +/* Get the 32-bit thread identifier for the current thread */ +extern DECLSPEC Uint32 SDLCALL SDL_ThreadID(void); + +/* Get the 32-bit thread identifier for the specified thread, + equivalent to SDL_ThreadID() if the specified thread is NULL. + */ +extern DECLSPEC Uint32 SDLCALL SDL_GetThreadID(SDL_Thread *thread); + +/* Wait for a thread to finish. + The return code for the thread function is placed in the area + pointed to by 'status', if 'status' is not NULL. + */ +extern DECLSPEC void SDLCALL SDL_WaitThread(SDL_Thread *thread, int *status); + +/* Forcefully kill a thread without worrying about its state */ +extern DECLSPEC void SDLCALL SDL_KillThread(SDL_Thread *thread); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_thread_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_timer.h b/reaction/engine/code/SDL12/include/SDL_timer.h new file mode 100644 index 00000000..d21159fe --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_timer.h @@ -0,0 +1,115 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifndef _SDL_timer_h +#define _SDL_timer_h + +/* Header for the SDL time management routines */ + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* This is the OS scheduler timeslice, in milliseconds */ +#define SDL_TIMESLICE 10 + +/* This is the maximum resolution of the SDL timer on all platforms */ +#define TIMER_RESOLUTION 10 /* Experimentally determined */ + +/* Get the number of milliseconds since the SDL library initialization. + * Note that this value wraps if the program runs for more than ~49 days. + */ +extern DECLSPEC Uint32 SDLCALL SDL_GetTicks(void); + +/* Wait a specified number of milliseconds before returning */ +extern DECLSPEC void SDLCALL SDL_Delay(Uint32 ms); + +/* Function prototype for the timer callback function */ +typedef Uint32 (SDLCALL *SDL_TimerCallback)(Uint32 interval); + +/* Set a callback to run after the specified number of milliseconds has + * elapsed. The callback function is passed the current timer interval + * and returns the next timer interval. If the returned value is the + * same as the one passed in, the periodic alarm continues, otherwise a + * new alarm is scheduled. If the callback returns 0, the periodic alarm + * is cancelled. + * + * To cancel a currently running timer, call SDL_SetTimer(0, NULL); + * + * The timer callback function may run in a different thread than your + * main code, and so shouldn't call any functions from within itself. + * + * The maximum resolution of this timer is 10 ms, which means that if + * you request a 16 ms timer, your callback will run approximately 20 ms + * later on an unloaded system. If you wanted to set a flag signaling + * a frame update at 30 frames per second (every 33 ms), you might set a + * timer for 30 ms: + * SDL_SetTimer((33/10)*10, flag_update); + * + * If you use this function, you need to pass SDL_INIT_TIMER to SDL_Init(). + * + * Under UNIX, you should not use raise or use SIGALRM and this function + * in the same program, as it is implemented using setitimer(). You also + * should not use this function in multi-threaded applications as signals + * to multi-threaded apps have undefined behavior in some implementations. + * + * This function returns 0 if successful, or -1 if there was an error. + */ +extern DECLSPEC int SDLCALL SDL_SetTimer(Uint32 interval, SDL_TimerCallback callback); + +/* New timer API, supports multiple timers + * Written by Stephane Peter + */ + +/* Function prototype for the new timer callback function. + * The callback function is passed the current timer interval and returns + * the next timer interval. If the returned value is the same as the one + * passed in, the periodic alarm continues, otherwise a new alarm is + * scheduled. If the callback returns 0, the periodic alarm is cancelled. + */ +typedef Uint32 (SDLCALL *SDL_NewTimerCallback)(Uint32 interval, void *param); + +/* Definition of the timer ID type */ +typedef struct _SDL_TimerID *SDL_TimerID; + +/* Add a new timer to the pool of timers already running. + Returns a timer ID, or NULL when an error occurs. + */ +extern DECLSPEC SDL_TimerID SDLCALL SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param); + +/* Remove one of the multiple timers knowing its ID. + * Returns a boolean value indicating success. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_RemoveTimer(SDL_TimerID t); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_timer_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_types.h b/reaction/engine/code/SDL12/include/SDL_types.h new file mode 100644 index 00000000..853b9ce4 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_types.h @@ -0,0 +1,24 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* DEPRECATED */ +#include "SDL_stdinc.h" diff --git a/reaction/engine/code/SDL12/include/SDL_version.h b/reaction/engine/code/SDL12/include/SDL_version.h new file mode 100644 index 00000000..262aa7b5 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_version.h @@ -0,0 +1,85 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* This header defines the current SDL version */ + +#ifndef _SDL_version_h +#define _SDL_version_h + +#include "SDL_stdinc.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL +*/ +#define SDL_MAJOR_VERSION 1 +#define SDL_MINOR_VERSION 2 +#define SDL_PATCHLEVEL 11 + +typedef struct SDL_version { + Uint8 major; + Uint8 minor; + Uint8 patch; +} SDL_version; + +/* This macro can be used to fill a version structure with the compile-time + * version of the SDL library. + */ +#define SDL_VERSION(X) \ +{ \ + (X)->major = SDL_MAJOR_VERSION; \ + (X)->minor = SDL_MINOR_VERSION; \ + (X)->patch = SDL_PATCHLEVEL; \ +} + +/* This macro turns the version numbers into a numeric value: + (1,2,3) -> (1203) + This assumes that there will never be more than 100 patchlevels +*/ +#define SDL_VERSIONNUM(X, Y, Z) \ + ((X)*1000 + (Y)*100 + (Z)) + +/* This is the version number macro for the current SDL version */ +#define SDL_COMPILEDVERSION \ + SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL) + +/* This macro will evaluate to true if compiled with SDL at least X.Y.Z */ +#define SDL_VERSION_ATLEAST(X, Y, Z) \ + (SDL_COMPILEDVERSION >= SDL_VERSIONNUM(X, Y, Z)) + +/* This function gets the version of the dynamically linked SDL library. + it should NOT be used to fill a version structure, instead you should + use the SDL_Version() macro. + */ +extern DECLSPEC const SDL_version * SDLCALL SDL_Linked_Version(void); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_version_h */ diff --git a/reaction/engine/code/SDL12/include/SDL_video.h b/reaction/engine/code/SDL12/include/SDL_video.h new file mode 100644 index 00000000..720022e3 --- /dev/null +++ b/reaction/engine/code/SDL12/include/SDL_video.h @@ -0,0 +1,889 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* Header file for access to the SDL raw framebuffer window */ + +#ifndef _SDL_video_h +#define _SDL_video_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_rwops.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Transparency definitions: These define alpha as the opacity of a surface */ +#define SDL_ALPHA_OPAQUE 255 +#define SDL_ALPHA_TRANSPARENT 0 + +/* Useful data types */ +typedef struct SDL_Rect { + Sint16 x, y; + Uint16 w, h; +} SDL_Rect; + +typedef struct SDL_Color { + Uint8 r; + Uint8 g; + Uint8 b; + Uint8 unused; +} SDL_Color; +#define SDL_Colour SDL_Color + +typedef struct SDL_Palette { + int ncolors; + SDL_Color *colors; +} SDL_Palette; + +/* Everything in the pixel format structure is read-only */ +typedef struct SDL_PixelFormat { + SDL_Palette *palette; + Uint8 BitsPerPixel; + Uint8 BytesPerPixel; + Uint8 Rloss; + Uint8 Gloss; + Uint8 Bloss; + Uint8 Aloss; + Uint8 Rshift; + Uint8 Gshift; + Uint8 Bshift; + Uint8 Ashift; + Uint32 Rmask; + Uint32 Gmask; + Uint32 Bmask; + Uint32 Amask; + + /* RGB color key information */ + Uint32 colorkey; + /* Alpha value information (per-surface alpha) */ + Uint8 alpha; +} SDL_PixelFormat; + +/* This structure should be treated as read-only, except for 'pixels', + which, if not NULL, contains the raw pixel data for the surface. +*/ +typedef struct SDL_Surface { + Uint32 flags; /* Read-only */ + SDL_PixelFormat *format; /* Read-only */ + int w, h; /* Read-only */ + Uint16 pitch; /* Read-only */ + void *pixels; /* Read-write */ + int offset; /* Private */ + + /* Hardware-specific surface info */ + struct private_hwdata *hwdata; + + /* clipping information */ + SDL_Rect clip_rect; /* Read-only */ + Uint32 unused1; /* for binary compatibility */ + + /* Allow recursive locks */ + Uint32 locked; /* Private */ + + /* info for fast blit mapping to other surfaces */ + struct SDL_BlitMap *map; /* Private */ + + /* format version, bumped at every change to invalidate blit maps */ + unsigned int format_version; /* Private */ + + /* Reference count -- used when freeing surface */ + int refcount; /* Read-mostly */ +} SDL_Surface; + +/* These are the currently supported flags for the SDL_surface */ +/* Available for SDL_CreateRGBSurface() or SDL_SetVideoMode() */ +#define SDL_SWSURFACE 0x00000000 /* Surface is in system memory */ +#define SDL_HWSURFACE 0x00000001 /* Surface is in video memory */ +#define SDL_ASYNCBLIT 0x00000004 /* Use asynchronous blits if possible */ +/* Available for SDL_SetVideoMode() */ +#define SDL_ANYFORMAT 0x10000000 /* Allow any video depth/pixel-format */ +#define SDL_HWPALETTE 0x20000000 /* Surface has exclusive palette */ +#define SDL_DOUBLEBUF 0x40000000 /* Set up double-buffered video mode */ +#define SDL_FULLSCREEN 0x80000000 /* Surface is a full screen display */ +#define SDL_OPENGL 0x00000002 /* Create an OpenGL rendering context */ +#define SDL_OPENGLBLIT 0x0000000A /* Create an OpenGL rendering context and use it for blitting */ +#define SDL_RESIZABLE 0x00000010 /* This video mode may be resized */ +#define SDL_NOFRAME 0x00000020 /* No window caption or edge frame */ +/* Used internally (read-only) */ +#define SDL_HWACCEL 0x00000100 /* Blit uses hardware acceleration */ +#define SDL_SRCCOLORKEY 0x00001000 /* Blit uses a source color key */ +#define SDL_RLEACCELOK 0x00002000 /* Private flag */ +#define SDL_RLEACCEL 0x00004000 /* Surface is RLE encoded */ +#define SDL_SRCALPHA 0x00010000 /* Blit uses source alpha blending */ +#define SDL_PREALLOC 0x01000000 /* Surface uses preallocated memory */ + +/* Evaluates to true if the surface needs to be locked before access */ +#define SDL_MUSTLOCK(surface) \ + (surface->offset || \ + ((surface->flags & (SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_RLEACCEL)) != 0)) + +/* typedef for private surface blitting functions */ +typedef int (*SDL_blit)(struct SDL_Surface *src, SDL_Rect *srcrect, + struct SDL_Surface *dst, SDL_Rect *dstrect); + + +/* Useful for determining the video hardware capabilities */ +typedef struct SDL_VideoInfo { + Uint32 hw_available :1; /* Flag: Can you create hardware surfaces? */ + Uint32 wm_available :1; /* Flag: Can you talk to a window manager? */ + Uint32 UnusedBits1 :6; + Uint32 UnusedBits2 :1; + Uint32 blit_hw :1; /* Flag: Accelerated blits HW --> HW */ + Uint32 blit_hw_CC :1; /* Flag: Accelerated blits with Colorkey */ + Uint32 blit_hw_A :1; /* Flag: Accelerated blits with Alpha */ + Uint32 blit_sw :1; /* Flag: Accelerated blits SW --> HW */ + Uint32 blit_sw_CC :1; /* Flag: Accelerated blits with Colorkey */ + Uint32 blit_sw_A :1; /* Flag: Accelerated blits with Alpha */ + Uint32 blit_fill :1; /* Flag: Accelerated color fill */ + Uint32 UnusedBits3 :16; + Uint32 video_mem; /* The total amount of video memory (in K) */ + SDL_PixelFormat *vfmt; /* Value: The format of the video surface */ + int current_w; /* Value: The current video mode width */ + int current_h; /* Value: The current video mode height */ +} SDL_VideoInfo; + + +/* The most common video overlay formats. + For an explanation of these pixel formats, see: + http://www.webartz.com/fourcc/indexyuv.htm + + For information on the relationship between color spaces, see: + http://www.neuro.sfc.keio.ac.jp/~aly/polygon/info/color-space-faq.html + */ +#define SDL_YV12_OVERLAY 0x32315659 /* Planar mode: Y + V + U (3 planes) */ +#define SDL_IYUV_OVERLAY 0x56555949 /* Planar mode: Y + U + V (3 planes) */ +#define SDL_YUY2_OVERLAY 0x32595559 /* Packed mode: Y0+U0+Y1+V0 (1 plane) */ +#define SDL_UYVY_OVERLAY 0x59565955 /* Packed mode: U0+Y0+V0+Y1 (1 plane) */ +#define SDL_YVYU_OVERLAY 0x55595659 /* Packed mode: Y0+V0+Y1+U0 (1 plane) */ + +/* The YUV hardware video overlay */ +typedef struct SDL_Overlay { + Uint32 format; /* Read-only */ + int w, h; /* Read-only */ + int planes; /* Read-only */ + Uint16 *pitches; /* Read-only */ + Uint8 **pixels; /* Read-write */ + + /* Hardware-specific surface info */ + struct private_yuvhwfuncs *hwfuncs; + struct private_yuvhwdata *hwdata; + + /* Special flags */ + Uint32 hw_overlay :1; /* Flag: This overlay hardware accelerated? */ + Uint32 UnusedBits :31; +} SDL_Overlay; + + +/* Public enumeration for setting the OpenGL window attributes. */ +typedef enum { + SDL_GL_RED_SIZE, + SDL_GL_GREEN_SIZE, + SDL_GL_BLUE_SIZE, + SDL_GL_ALPHA_SIZE, + SDL_GL_BUFFER_SIZE, + SDL_GL_DOUBLEBUFFER, + SDL_GL_DEPTH_SIZE, + SDL_GL_STENCIL_SIZE, + SDL_GL_ACCUM_RED_SIZE, + SDL_GL_ACCUM_GREEN_SIZE, + SDL_GL_ACCUM_BLUE_SIZE, + SDL_GL_ACCUM_ALPHA_SIZE, + SDL_GL_STEREO, + SDL_GL_MULTISAMPLEBUFFERS, + SDL_GL_MULTISAMPLESAMPLES, + SDL_GL_ACCELERATED_VISUAL, + SDL_GL_SWAP_CONTROL +} SDL_GLattr; + +/* flags for SDL_SetPalette() */ +#define SDL_LOGPAL 0x01 +#define SDL_PHYSPAL 0x02 + +/* Function prototypes */ + +/* These functions are used internally, and should not be used unless you + * have a specific need to specify the video driver you want to use. + * You should normally use SDL_Init() or SDL_InitSubSystem(). + * + * SDL_VideoInit() initializes the video subsystem -- sets up a connection + * to the window manager, etc, and determines the current video mode and + * pixel format, but does not initialize a window or graphics mode. + * Note that event handling is activated by this routine. + * + * If you use both sound and video in your application, you need to call + * SDL_Init() before opening the sound device, otherwise under Win32 DirectX, + * you won't be able to set full-screen display modes. + */ +extern DECLSPEC int SDLCALL SDL_VideoInit(const char *driver_name, Uint32 flags); +extern DECLSPEC void SDLCALL SDL_VideoQuit(void); + +/* This function fills the given character buffer with the name of the + * video driver, and returns a pointer to it if the video driver has + * been initialized. It returns NULL if no driver has been initialized. + */ +extern DECLSPEC char * SDLCALL SDL_VideoDriverName(char *namebuf, int maxlen); + +/* + * This function returns a pointer to the current display surface. + * If SDL is doing format conversion on the display surface, this + * function returns the publicly visible surface, not the real video + * surface. + */ +extern DECLSPEC SDL_Surface * SDLCALL SDL_GetVideoSurface(void); + +/* + * This function returns a read-only pointer to information about the + * video hardware. If this is called before SDL_SetVideoMode(), the 'vfmt' + * member of the returned structure will contain the pixel format of the + * "best" video mode. + */ +extern DECLSPEC const SDL_VideoInfo * SDLCALL SDL_GetVideoInfo(void); + +/* + * Check to see if a particular video mode is supported. + * It returns 0 if the requested mode is not supported under any bit depth, + * or returns the bits-per-pixel of the closest available mode with the + * given width and height. If this bits-per-pixel is different from the + * one used when setting the video mode, SDL_SetVideoMode() will succeed, + * but will emulate the requested bits-per-pixel with a shadow surface. + * + * The arguments to SDL_VideoModeOK() are the same ones you would pass to + * SDL_SetVideoMode() + */ +extern DECLSPEC int SDLCALL SDL_VideoModeOK(int width, int height, int bpp, Uint32 flags); + +/* + * Return a pointer to an array of available screen dimensions for the + * given format and video flags, sorted largest to smallest. Returns + * NULL if there are no dimensions available for a particular format, + * or (SDL_Rect **)-1 if any dimension is okay for the given format. + * + * If 'format' is NULL, the mode list will be for the format given + * by SDL_GetVideoInfo()->vfmt + */ +extern DECLSPEC SDL_Rect ** SDLCALL SDL_ListModes(SDL_PixelFormat *format, Uint32 flags); + +/* + * Set up a video mode with the specified width, height and bits-per-pixel. + * + * If 'bpp' is 0, it is treated as the current display bits per pixel. + * + * If SDL_ANYFORMAT is set in 'flags', the SDL library will try to set the + * requested bits-per-pixel, but will return whatever video pixel format is + * available. The default is to emulate the requested pixel format if it + * is not natively available. + * + * If SDL_HWSURFACE is set in 'flags', the video surface will be placed in + * video memory, if possible, and you may have to call SDL_LockSurface() + * in order to access the raw framebuffer. Otherwise, the video surface + * will be created in system memory. + * + * If SDL_ASYNCBLIT is set in 'flags', SDL will try to perform rectangle + * updates asynchronously, but you must always lock before accessing pixels. + * SDL will wait for updates to complete before returning from the lock. + * + * If SDL_HWPALETTE is set in 'flags', the SDL library will guarantee + * that the colors set by SDL_SetColors() will be the colors you get. + * Otherwise, in 8-bit mode, SDL_SetColors() may not be able to set all + * of the colors exactly the way they are requested, and you should look + * at the video surface structure to determine the actual palette. + * If SDL cannot guarantee that the colors you request can be set, + * i.e. if the colormap is shared, then the video surface may be created + * under emulation in system memory, overriding the SDL_HWSURFACE flag. + * + * If SDL_FULLSCREEN is set in 'flags', the SDL library will try to set + * a fullscreen video mode. The default is to create a windowed mode + * if the current graphics system has a window manager. + * If the SDL library is able to set a fullscreen video mode, this flag + * will be set in the surface that is returned. + * + * If SDL_DOUBLEBUF is set in 'flags', the SDL library will try to set up + * two surfaces in video memory and swap between them when you call + * SDL_Flip(). This is usually slower than the normal single-buffering + * scheme, but prevents "tearing" artifacts caused by modifying video + * memory while the monitor is refreshing. It should only be used by + * applications that redraw the entire screen on every update. + * + * If SDL_RESIZABLE is set in 'flags', the SDL library will allow the + * window manager, if any, to resize the window at runtime. When this + * occurs, SDL will send a SDL_VIDEORESIZE event to you application, + * and you must respond to the event by re-calling SDL_SetVideoMode() + * with the requested size (or another size that suits the application). + * + * If SDL_NOFRAME is set in 'flags', the SDL library will create a window + * without any title bar or frame decoration. Fullscreen video modes have + * this flag set automatically. + * + * This function returns the video framebuffer surface, or NULL if it fails. + * + * If you rely on functionality provided by certain video flags, check the + * flags of the returned surface to make sure that functionality is available. + * SDL will fall back to reduced functionality if the exact flags you wanted + * are not available. + */ +extern DECLSPEC SDL_Surface * SDLCALL SDL_SetVideoMode + (int width, int height, int bpp, Uint32 flags); + +/* + * Makes sure the given list of rectangles is updated on the given screen. + * If 'x', 'y', 'w' and 'h' are all 0, SDL_UpdateRect will update the entire + * screen. + * These functions should not be called while 'screen' is locked. + */ +extern DECLSPEC void SDLCALL SDL_UpdateRects + (SDL_Surface *screen, int numrects, SDL_Rect *rects); +extern DECLSPEC void SDLCALL SDL_UpdateRect + (SDL_Surface *screen, Sint32 x, Sint32 y, Uint32 w, Uint32 h); + +/* + * On hardware that supports double-buffering, this function sets up a flip + * and returns. The hardware will wait for vertical retrace, and then swap + * video buffers before the next video surface blit or lock will return. + * On hardware that doesn not support double-buffering, this is equivalent + * to calling SDL_UpdateRect(screen, 0, 0, 0, 0); + * The SDL_DOUBLEBUF flag must have been passed to SDL_SetVideoMode() when + * setting the video mode for this function to perform hardware flipping. + * This function returns 0 if successful, or -1 if there was an error. + */ +extern DECLSPEC int SDLCALL SDL_Flip(SDL_Surface *screen); + +/* + * Set the gamma correction for each of the color channels. + * The gamma values range (approximately) between 0.1 and 10.0 + * + * If this function isn't supported directly by the hardware, it will + * be emulated using gamma ramps, if available. If successful, this + * function returns 0, otherwise it returns -1. + */ +extern DECLSPEC int SDLCALL SDL_SetGamma(float red, float green, float blue); + +/* + * Set the gamma translation table for the red, green, and blue channels + * of the video hardware. Each table is an array of 256 16-bit quantities, + * representing a mapping between the input and output for that channel. + * The input is the index into the array, and the output is the 16-bit + * gamma value at that index, scaled to the output color precision. + * + * You may pass NULL for any of the channels to leave it unchanged. + * If the call succeeds, it will return 0. If the display driver or + * hardware does not support gamma translation, or otherwise fails, + * this function will return -1. + */ +extern DECLSPEC int SDLCALL SDL_SetGammaRamp(const Uint16 *red, const Uint16 *green, const Uint16 *blue); + +/* + * Retrieve the current values of the gamma translation tables. + * + * You must pass in valid pointers to arrays of 256 16-bit quantities. + * Any of the pointers may be NULL to ignore that channel. + * If the call succeeds, it will return 0. If the display driver or + * hardware does not support gamma translation, or otherwise fails, + * this function will return -1. + */ +extern DECLSPEC int SDLCALL SDL_GetGammaRamp(Uint16 *red, Uint16 *green, Uint16 *blue); + +/* + * Sets a portion of the colormap for the given 8-bit surface. If 'surface' + * is not a palettized surface, this function does nothing, returning 0. + * If all of the colors were set as passed to SDL_SetColors(), it will + * return 1. If not all the color entries were set exactly as given, + * it will return 0, and you should look at the surface palette to + * determine the actual color palette. + * + * When 'surface' is the surface associated with the current display, the + * display colormap will be updated with the requested colors. If + * SDL_HWPALETTE was set in SDL_SetVideoMode() flags, SDL_SetColors() + * will always return 1, and the palette is guaranteed to be set the way + * you desire, even if the window colormap has to be warped or run under + * emulation. + */ +extern DECLSPEC int SDLCALL SDL_SetColors(SDL_Surface *surface, + SDL_Color *colors, int firstcolor, int ncolors); + +/* + * Sets a portion of the colormap for a given 8-bit surface. + * 'flags' is one or both of: + * SDL_LOGPAL -- set logical palette, which controls how blits are mapped + * to/from the surface, + * SDL_PHYSPAL -- set physical palette, which controls how pixels look on + * the screen + * Only screens have physical palettes. Separate change of physical/logical + * palettes is only possible if the screen has SDL_HWPALETTE set. + * + * The return value is 1 if all colours could be set as requested, and 0 + * otherwise. + * + * SDL_SetColors() is equivalent to calling this function with + * flags = (SDL_LOGPAL|SDL_PHYSPAL). + */ +extern DECLSPEC int SDLCALL SDL_SetPalette(SDL_Surface *surface, int flags, + SDL_Color *colors, int firstcolor, + int ncolors); + +/* + * Maps an RGB triple to an opaque pixel value for a given pixel format + */ +extern DECLSPEC Uint32 SDLCALL SDL_MapRGB + (SDL_PixelFormat *format, Uint8 r, Uint8 g, Uint8 b); + +/* + * Maps an RGBA quadruple to a pixel value for a given pixel format + */ +extern DECLSPEC Uint32 SDLCALL SDL_MapRGBA(SDL_PixelFormat *format, + Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* + * Maps a pixel value into the RGB components for a given pixel format + */ +extern DECLSPEC void SDLCALL SDL_GetRGB(Uint32 pixel, SDL_PixelFormat *fmt, + Uint8 *r, Uint8 *g, Uint8 *b); + +/* + * Maps a pixel value into the RGBA components for a given pixel format + */ +extern DECLSPEC void SDLCALL SDL_GetRGBA(Uint32 pixel, SDL_PixelFormat *fmt, + Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a); + +/* + * Allocate and free an RGB surface (must be called after SDL_SetVideoMode) + * If the depth is 4 or 8 bits, an empty palette is allocated for the surface. + * If the depth is greater than 8 bits, the pixel format is set using the + * flags '[RGB]mask'. + * If the function runs out of memory, it will return NULL. + * + * The 'flags' tell what kind of surface to create. + * SDL_SWSURFACE means that the surface should be created in system memory. + * SDL_HWSURFACE means that the surface should be created in video memory, + * with the same format as the display surface. This is useful for surfaces + * that will not change much, to take advantage of hardware acceleration + * when being blitted to the display surface. + * SDL_ASYNCBLIT means that SDL will try to perform asynchronous blits with + * this surface, but you must always lock it before accessing the pixels. + * SDL will wait for current blits to finish before returning from the lock. + * SDL_SRCCOLORKEY indicates that the surface will be used for colorkey blits. + * If the hardware supports acceleration of colorkey blits between + * two surfaces in video memory, SDL will try to place the surface in + * video memory. If this isn't possible or if there is no hardware + * acceleration available, the surface will be placed in system memory. + * SDL_SRCALPHA means that the surface will be used for alpha blits and + * if the hardware supports hardware acceleration of alpha blits between + * two surfaces in video memory, to place the surface in video memory + * if possible, otherwise it will be placed in system memory. + * If the surface is created in video memory, blits will be _much_ faster, + * but the surface format must be identical to the video surface format, + * and the only way to access the pixels member of the surface is to use + * the SDL_LockSurface() and SDL_UnlockSurface() calls. + * If the requested surface actually resides in video memory, SDL_HWSURFACE + * will be set in the flags member of the returned surface. If for some + * reason the surface could not be placed in video memory, it will not have + * the SDL_HWSURFACE flag set, and will be created in system memory instead. + */ +#define SDL_AllocSurface SDL_CreateRGBSurface +extern DECLSPEC SDL_Surface * SDLCALL SDL_CreateRGBSurface + (Uint32 flags, int width, int height, int depth, + Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask); +extern DECLSPEC SDL_Surface * SDLCALL SDL_CreateRGBSurfaceFrom(void *pixels, + int width, int height, int depth, int pitch, + Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask); +extern DECLSPEC void SDLCALL SDL_FreeSurface(SDL_Surface *surface); + +/* + * SDL_LockSurface() sets up a surface for directly accessing the pixels. + * Between calls to SDL_LockSurface()/SDL_UnlockSurface(), you can write + * to and read from 'surface->pixels', using the pixel format stored in + * 'surface->format'. Once you are done accessing the surface, you should + * use SDL_UnlockSurface() to release it. + * + * Not all surfaces require locking. If SDL_MUSTLOCK(surface) evaluates + * to 0, then you can read and write to the surface at any time, and the + * pixel format of the surface will not change. In particular, if the + * SDL_HWSURFACE flag is not given when calling SDL_SetVideoMode(), you + * will not need to lock the display surface before accessing it. + * + * No operating system or library calls should be made between lock/unlock + * pairs, as critical system locks may be held during this time. + * + * SDL_LockSurface() returns 0, or -1 if the surface couldn't be locked. + */ +extern DECLSPEC int SDLCALL SDL_LockSurface(SDL_Surface *surface); +extern DECLSPEC void SDLCALL SDL_UnlockSurface(SDL_Surface *surface); + +/* + * Load a surface from a seekable SDL data source (memory or file.) + * If 'freesrc' is non-zero, the source will be closed after being read. + * Returns the new surface, or NULL if there was an error. + * The new surface should be freed with SDL_FreeSurface(). + */ +extern DECLSPEC SDL_Surface * SDLCALL SDL_LoadBMP_RW(SDL_RWops *src, int freesrc); + +/* Convenience macro -- load a surface from a file */ +#define SDL_LoadBMP(file) SDL_LoadBMP_RW(SDL_RWFromFile(file, "rb"), 1) + +/* + * Save a surface to a seekable SDL data source (memory or file.) + * If 'freedst' is non-zero, the source will be closed after being written. + * Returns 0 if successful or -1 if there was an error. + */ +extern DECLSPEC int SDLCALL SDL_SaveBMP_RW + (SDL_Surface *surface, SDL_RWops *dst, int freedst); + +/* Convenience macro -- save a surface to a file */ +#define SDL_SaveBMP(surface, file) \ + SDL_SaveBMP_RW(surface, SDL_RWFromFile(file, "wb"), 1) + +/* + * Sets the color key (transparent pixel) in a blittable surface. + * If 'flag' is SDL_SRCCOLORKEY (optionally OR'd with SDL_RLEACCEL), + * 'key' will be the transparent pixel in the source image of a blit. + * SDL_RLEACCEL requests RLE acceleration for the surface if present, + * and removes RLE acceleration if absent. + * If 'flag' is 0, this function clears any current color key. + * This function returns 0, or -1 if there was an error. + */ +extern DECLSPEC int SDLCALL SDL_SetColorKey + (SDL_Surface *surface, Uint32 flag, Uint32 key); + +/* + * This function sets the alpha value for the entire surface, as opposed to + * using the alpha component of each pixel. This value measures the range + * of transparency of the surface, 0 being completely transparent to 255 + * being completely opaque. An 'alpha' value of 255 causes blits to be + * opaque, the source pixels copied to the destination (the default). Note + * that per-surface alpha can be combined with colorkey transparency. + * + * If 'flag' is 0, alpha blending is disabled for the surface. + * If 'flag' is SDL_SRCALPHA, alpha blending is enabled for the surface. + * OR:ing the flag with SDL_RLEACCEL requests RLE acceleration for the + * surface; if SDL_RLEACCEL is not specified, the RLE accel will be removed. + * + * The 'alpha' parameter is ignored for surfaces that have an alpha channel. + */ +extern DECLSPEC int SDLCALL SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha); + +/* + * Sets the clipping rectangle for the destination surface in a blit. + * + * If the clip rectangle is NULL, clipping will be disabled. + * If the clip rectangle doesn't intersect the surface, the function will + * return SDL_FALSE and blits will be completely clipped. Otherwise the + * function returns SDL_TRUE and blits to the surface will be clipped to + * the intersection of the surface area and the clipping rectangle. + * + * Note that blits are automatically clipped to the edges of the source + * and destination surfaces. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_SetClipRect(SDL_Surface *surface, const SDL_Rect *rect); + +/* + * Gets the clipping rectangle for the destination surface in a blit. + * 'rect' must be a pointer to a valid rectangle which will be filled + * with the correct values. + */ +extern DECLSPEC void SDLCALL SDL_GetClipRect(SDL_Surface *surface, SDL_Rect *rect); + +/* + * Creates a new surface of the specified format, and then copies and maps + * the given surface to it so the blit of the converted surface will be as + * fast as possible. If this function fails, it returns NULL. + * + * The 'flags' parameter is passed to SDL_CreateRGBSurface() and has those + * semantics. You can also pass SDL_RLEACCEL in the flags parameter and + * SDL will try to RLE accelerate colorkey and alpha blits in the resulting + * surface. + * + * This function is used internally by SDL_DisplayFormat(). + */ +extern DECLSPEC SDL_Surface * SDLCALL SDL_ConvertSurface + (SDL_Surface *src, SDL_PixelFormat *fmt, Uint32 flags); + +/* + * This performs a fast blit from the source surface to the destination + * surface. It assumes that the source and destination rectangles are + * the same size. If either 'srcrect' or 'dstrect' are NULL, the entire + * surface (src or dst) is copied. The final blit rectangles are saved + * in 'srcrect' and 'dstrect' after all clipping is performed. + * If the blit is successful, it returns 0, otherwise it returns -1. + * + * The blit function should not be called on a locked surface. + * + * The blit semantics for surfaces with and without alpha and colorkey + * are defined as follows: + * + * RGBA->RGB: + * SDL_SRCALPHA set: + * alpha-blend (using alpha-channel). + * SDL_SRCCOLORKEY ignored. + * SDL_SRCALPHA not set: + * copy RGB. + * if SDL_SRCCOLORKEY set, only copy the pixels matching the + * RGB values of the source colour key, ignoring alpha in the + * comparison. + * + * RGB->RGBA: + * SDL_SRCALPHA set: + * alpha-blend (using the source per-surface alpha value); + * set destination alpha to opaque. + * SDL_SRCALPHA not set: + * copy RGB, set destination alpha to source per-surface alpha value. + * both: + * if SDL_SRCCOLORKEY set, only copy the pixels matching the + * source colour key. + * + * RGBA->RGBA: + * SDL_SRCALPHA set: + * alpha-blend (using the source alpha channel) the RGB values; + * leave destination alpha untouched. [Note: is this correct?] + * SDL_SRCCOLORKEY ignored. + * SDL_SRCALPHA not set: + * copy all of RGBA to the destination. + * if SDL_SRCCOLORKEY set, only copy the pixels matching the + * RGB values of the source colour key, ignoring alpha in the + * comparison. + * + * RGB->RGB: + * SDL_SRCALPHA set: + * alpha-blend (using the source per-surface alpha value). + * SDL_SRCALPHA not set: + * copy RGB. + * both: + * if SDL_SRCCOLORKEY set, only copy the pixels matching the + * source colour key. + * + * If either of the surfaces were in video memory, and the blit returns -2, + * the video memory was lost, so it should be reloaded with artwork and + * re-blitted: + while ( SDL_BlitSurface(image, imgrect, screen, dstrect) == -2 ) { + while ( SDL_LockSurface(image) < 0 ) + Sleep(10); + -- Write image pixels to image->pixels -- + SDL_UnlockSurface(image); + } + * This happens under DirectX 5.0 when the system switches away from your + * fullscreen application. The lock will also fail until you have access + * to the video memory again. + */ +/* You should call SDL_BlitSurface() unless you know exactly how SDL + blitting works internally and how to use the other blit functions. +*/ +#define SDL_BlitSurface SDL_UpperBlit + +/* This is the public blit function, SDL_BlitSurface(), and it performs + rectangle validation and clipping before passing it to SDL_LowerBlit() +*/ +extern DECLSPEC int SDLCALL SDL_UpperBlit + (SDL_Surface *src, SDL_Rect *srcrect, + SDL_Surface *dst, SDL_Rect *dstrect); +/* This is a semi-private blit function and it performs low-level surface + blitting only. +*/ +extern DECLSPEC int SDLCALL SDL_LowerBlit + (SDL_Surface *src, SDL_Rect *srcrect, + SDL_Surface *dst, SDL_Rect *dstrect); + +/* + * This function performs a fast fill of the given rectangle with 'color' + * The given rectangle is clipped to the destination surface clip area + * and the final fill rectangle is saved in the passed in pointer. + * If 'dstrect' is NULL, the whole surface will be filled with 'color' + * The color should be a pixel of the format used by the surface, and + * can be generated by the SDL_MapRGB() function. + * This function returns 0 on success, or -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_FillRect + (SDL_Surface *dst, SDL_Rect *dstrect, Uint32 color); + +/* + * This function takes a surface and copies it to a new surface of the + * pixel format and colors of the video framebuffer, suitable for fast + * blitting onto the display surface. It calls SDL_ConvertSurface() + * + * If you want to take advantage of hardware colorkey or alpha blit + * acceleration, you should set the colorkey and alpha value before + * calling this function. + * + * If the conversion fails or runs out of memory, it returns NULL + */ +extern DECLSPEC SDL_Surface * SDLCALL SDL_DisplayFormat(SDL_Surface *surface); + +/* + * This function takes a surface and copies it to a new surface of the + * pixel format and colors of the video framebuffer (if possible), + * suitable for fast alpha blitting onto the display surface. + * The new surface will always have an alpha channel. + * + * If you want to take advantage of hardware colorkey or alpha blit + * acceleration, you should set the colorkey and alpha value before + * calling this function. + * + * If the conversion fails or runs out of memory, it returns NULL + */ +extern DECLSPEC SDL_Surface * SDLCALL SDL_DisplayFormatAlpha(SDL_Surface *surface); + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* YUV video surface overlay functions */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* This function creates a video output overlay + Calling the returned surface an overlay is something of a misnomer because + the contents of the display surface underneath the area where the overlay + is shown is undefined - it may be overwritten with the converted YUV data. +*/ +extern DECLSPEC SDL_Overlay * SDLCALL SDL_CreateYUVOverlay(int width, int height, + Uint32 format, SDL_Surface *display); + +/* Lock an overlay for direct access, and unlock it when you are done */ +extern DECLSPEC int SDLCALL SDL_LockYUVOverlay(SDL_Overlay *overlay); +extern DECLSPEC void SDLCALL SDL_UnlockYUVOverlay(SDL_Overlay *overlay); + +/* Blit a video overlay to the display surface. + The contents of the video surface underneath the blit destination are + not defined. + The width and height of the destination rectangle may be different from + that of the overlay, but currently only 2x scaling is supported. +*/ +extern DECLSPEC int SDLCALL SDL_DisplayYUVOverlay(SDL_Overlay *overlay, SDL_Rect *dstrect); + +/* Free a video overlay */ +extern DECLSPEC void SDLCALL SDL_FreeYUVOverlay(SDL_Overlay *overlay); + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* OpenGL support functions. */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Dynamically load an OpenGL library, or the default one if path is NULL + * + * If you do this, you need to retrieve all of the GL functions used in + * your program from the dynamic library using SDL_GL_GetProcAddress(). + */ +extern DECLSPEC int SDLCALL SDL_GL_LoadLibrary(const char *path); + +/* + * Get the address of a GL function + */ +extern DECLSPEC void * SDLCALL SDL_GL_GetProcAddress(const char* proc); + +/* + * Set an attribute of the OpenGL subsystem before intialization. + */ +extern DECLSPEC int SDLCALL SDL_GL_SetAttribute(SDL_GLattr attr, int value); + +/* + * Get an attribute of the OpenGL subsystem from the windowing + * interface, such as glX. This is of course different from getting + * the values from SDL's internal OpenGL subsystem, which only + * stores the values you request before initialization. + * + * Developers should track the values they pass into SDL_GL_SetAttribute + * themselves if they want to retrieve these values. + */ +extern DECLSPEC int SDLCALL SDL_GL_GetAttribute(SDL_GLattr attr, int* value); + +/* + * Swap the OpenGL buffers, if double-buffering is supported. + */ +extern DECLSPEC void SDLCALL SDL_GL_SwapBuffers(void); + +/* + * Internal functions that should not be called unless you have read + * and understood the source code for these functions. + */ +extern DECLSPEC void SDLCALL SDL_GL_UpdateRects(int numrects, SDL_Rect* rects); +extern DECLSPEC void SDLCALL SDL_GL_Lock(void); +extern DECLSPEC void SDLCALL SDL_GL_Unlock(void); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* These functions allow interaction with the window manager, if any. */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Sets/Gets the title and icon text of the display window (UTF-8 encoded) + */ +extern DECLSPEC void SDLCALL SDL_WM_SetCaption(const char *title, const char *icon); +extern DECLSPEC void SDLCALL SDL_WM_GetCaption(char **title, char **icon); + +/* + * Sets the icon for the display window. + * This function must be called before the first call to SDL_SetVideoMode(). + * It takes an icon surface, and a mask in MSB format. + * If 'mask' is NULL, the entire icon surface will be used as the icon. + */ +extern DECLSPEC void SDLCALL SDL_WM_SetIcon(SDL_Surface *icon, Uint8 *mask); + +/* + * This function iconifies the window, and returns 1 if it succeeded. + * If the function succeeds, it generates an SDL_APPACTIVE loss event. + * This function is a noop and returns 0 in non-windowed environments. + */ +extern DECLSPEC int SDLCALL SDL_WM_IconifyWindow(void); + +/* + * Toggle fullscreen mode without changing the contents of the screen. + * If the display surface does not require locking before accessing + * the pixel information, then the memory pointers will not change. + * + * If this function was able to toggle fullscreen mode (change from + * running in a window to fullscreen, or vice-versa), it will return 1. + * If it is not implemented, or fails, it returns 0. + * + * The next call to SDL_SetVideoMode() will set the mode fullscreen + * attribute based on the flags parameter - if SDL_FULLSCREEN is not + * set, then the display will be windowed by default where supported. + * + * This is currently only implemented in the X11 video driver. + */ +extern DECLSPEC int SDLCALL SDL_WM_ToggleFullScreen(SDL_Surface *surface); + +/* + * This function allows you to set and query the input grab state of + * the application. It returns the new input grab state. + */ +typedef enum { + SDL_GRAB_QUERY = -1, + SDL_GRAB_OFF = 0, + SDL_GRAB_ON = 1, + SDL_GRAB_FULLSCREEN /* Used internally */ +} SDL_GrabMode; +/* + * Grabbing means that the mouse is confined to the application window, + * and nearly all keyboard input is passed directly to the application, + * and not interpreted by a window manager, if any. + */ +extern DECLSPEC SDL_GrabMode SDLCALL SDL_WM_GrabInput(SDL_GrabMode mode); + +/* Not in public API at the moment - do not use! */ +extern DECLSPEC int SDLCALL SDL_SoftStretch(SDL_Surface *src, SDL_Rect *srcrect, + SDL_Surface *dst, SDL_Rect *dstrect); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_video_h */ diff --git a/reaction/engine/code/SDL12/include/begin_code.h b/reaction/engine/code/SDL12/include/begin_code.h new file mode 100644 index 00000000..40279337 --- /dev/null +++ b/reaction/engine/code/SDL12/include/begin_code.h @@ -0,0 +1,150 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* This file sets things up for C dynamic library function definitions, + static inlined functions, and structures aligned at 4-byte alignment. + If you don't like ugly C preprocessor code, don't look at this file. :) +*/ + +/* This shouldn't be nested -- included it around code only. */ +#ifdef _begin_code_h +#error Nested inclusion of begin_code.h +#endif +#define _begin_code_h + +/* Some compilers use a special export keyword */ +#ifndef DECLSPEC +# if defined(__BEOS__) +# if defined(__GNUC__) +# define DECLSPEC __declspec(dllexport) +# else +# define DECLSPEC __declspec(export) +# endif +# elif defined(__WIN32__) +# ifdef __BORLANDC__ +# ifdef BUILD_SDL +# define DECLSPEC +# else +# define DECLSPEC __declspec(dllimport) +# endif +# else +# define DECLSPEC __declspec(dllexport) +# endif +# elif defined(__OS2__) +# ifdef __WATCOMC__ +# ifdef BUILD_SDL +# define DECLSPEC __declspec(dllexport) +# else +# define DECLSPEC +# endif +# else +# define DECLSPEC +# endif +# else +# if defined(__GNUC__) && __GNUC__ >= 4 +# define DECLSPEC __attribute__ ((visibility("default"))) +# else +# define DECLSPEC +# endif +# endif +#endif + +/* By default SDL uses the C calling convention */ +#ifndef SDLCALL +#if defined(__WIN32__) && !defined(__GNUC__) +#define SDLCALL __cdecl +#else +#ifdef __OS2__ +/* But on OS/2, we use the _System calling convention */ +/* to be compatible with every compiler */ +#define SDLCALL _System +#else +#define SDLCALL +#endif +#endif +#endif /* SDLCALL */ + +/* Removed DECLSPEC on Symbian OS because SDL cannot be a DLL in EPOC */ +#ifdef __SYMBIAN32__ +#undef DECLSPEC +#define DECLSPEC +#endif /* __SYMBIAN32__ */ + +/* Force structure packing at 4 byte alignment. + This is necessary if the header is included in code which has structure + packing set to an alternate value, say for loading structures from disk. + The packing is reset to the previous value in close_code.h + */ +#if defined(_MSC_VER) || defined(__MWERKS__) || defined(__BORLANDC__) +#ifdef _MSC_VER +#pragma warning(disable: 4103) +#endif +#ifdef __BORLANDC__ +#pragma nopackwarning +#endif +#pragma pack(push,4) +#elif (defined(__MWERKS__) && defined(__MACOS__)) +#pragma options align=mac68k4byte +#pragma enumsalwaysint on +#endif /* Compiler needs structure packing set */ + +/* Set up compiler-specific options for inlining functions */ +#ifndef SDL_INLINE_OKAY +#ifdef __GNUC__ +#define SDL_INLINE_OKAY +#else +/* Add any special compiler-specific cases here */ +#if defined(_MSC_VER) || defined(__BORLANDC__) || \ + defined(__DMC__) || defined(__SC__) || \ + defined(__WATCOMC__) || defined(__LCC__) || \ + defined(__DECC) +#ifndef __inline__ +#define __inline__ __inline +#endif +#define SDL_INLINE_OKAY +#else +#if !defined(__MRC__) && !defined(_SGI_SOURCE) +#define __inline__ inline +#define SDL_INLINE_OKAY +#endif /* Not a funky compiler */ +#endif /* Visual C++ */ +#endif /* GNU C */ +#endif /* SDL_INLINE_OKAY */ + +/* If inlining isn't supported, remove "__inline__", turning static + inlined functions into static functions (resulting in code bloat + in all files which include the offending header files) +*/ +#ifndef SDL_INLINE_OKAY +#define __inline__ +#endif + +/* Apparently this is needed by several Windows compilers */ +#if !defined(__MACH__) +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif /* NULL */ +#endif /* ! Mac OS X - breaks precompiled headers */ diff --git a/reaction/engine/code/SDL12/include/close_code.h b/reaction/engine/code/SDL12/include/close_code.h new file mode 100644 index 00000000..afbb6504 --- /dev/null +++ b/reaction/engine/code/SDL12/include/close_code.h @@ -0,0 +1,41 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/* This file reverses the effects of begin_code.h and should be included + after you finish any function and structure declarations in your headers +*/ + +#undef _begin_code_h + +/* Reset structure packing at previous byte alignment */ +#if defined(_MSC_VER) || defined(__MWERKS__) || defined(__WATCOMC__) || defined(__BORLANDC__) +#ifdef __BORLANDC__ +#pragma nopackwarning +#endif +#if (defined(__MWERKS__) && defined(__MACOS__)) +#pragma options align=reset +#pragma enumsalwaysint reset +#else +#pragma pack(pop) +#endif +#endif /* Compiler needs structure packing set */ + diff --git a/reaction/engine/code/asm/ftola.s b/reaction/engine/code/asm/ftola.s new file mode 100644 index 00000000..7e9c523e --- /dev/null +++ b/reaction/engine/code/asm/ftola.s @@ -0,0 +1,160 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +// +// qftol -- fast floating point to long conversion. +// + +// 23/09/05 Ported to gas by intel2gas, best supporting actor Tim Angus +// + +#include "qasm.h" + +#if id386 + +.data + +temp: .single 0.0 +fpucw: .long 0 + +// Precision Control Field , 2 bits / 0x0300 +// PC24 0x0000 Single precision (24 bits). +// PC53 0x0200 Double precision (53 bits). +// PC64 0x0300 Extended precision (64 bits). + +// Rounding Control Field, 2 bits / 0x0C00 +// RCN 0x0000 Rounding to nearest (even). +// RCD 0x0400 Rounding down (directed, minus). +// RCU 0x0800 Rounding up (directed plus). +// RC0 0x0C00 Rounding towards zero (chop mode). + + +// rounding towards nearest (even) +cw027F: .long 0x027F +cw037F: .long 0x037F + +// rounding towards zero (chop mode) +cw0E7F: .long 0x0E7F +cw0F7F: .long 0x0F7F + + +.text + +// +// int qftol( void ) - default control word +// + +.globl C(qftol) + +C(qftol): + fistpl temp + movl temp,%eax + ret + + +// +// int qftol027F( void ) - DirectX FPU +// + +.globl C(qftol027F) + +C(qftol027F): + fnstcw fpucw + fldcw cw027F + fistpl temp + fldcw fpucw + movl temp,%eax + ret + +// +// int qftol037F( void ) - Linux FPU +// + +.globl C(qftol037F) + +C(qftol037F): + fnstcw fpucw + fldcw cw037F + fistpl temp + fldcw fpucw + movl temp,%eax + ret + + +// +// int qftol0F7F( void ) - ANSI +// + +.globl C(qftol0F7F) + +C(qftol0F7F): + fnstcw fpucw + fldcw cw0F7F + fistpl temp + fldcw fpucw + movl temp,%eax + ret + +// +// int qftol0E7F( void ) +// + +.globl C(qftol0E7F) + +C(qftol0E7F): + fnstcw fpucw + fldcw cw0E7F + fistpl temp + fldcw fpucw + movl temp,%eax + ret + + + +// +// long Q_ftol( float q ) +// + +.globl C(Q_ftol) + +C(Q_ftol): + flds 4(%esp) + fistpl temp + movl temp,%eax + ret + + +// +// long qftol0F7F( float q ) - Linux FPU +// + +.globl C(Q_ftol0F7F) + +C(Q_ftol0F7F): + fnstcw fpucw + flds 4(%esp) + fldcw cw0F7F + fistpl temp + fldcw fpucw + movl temp,%eax + ret +#endif diff --git a/reaction/engine/code/asm/matha.s b/reaction/engine/code/asm/matha.s new file mode 100644 index 00000000..3bc22204 --- /dev/null +++ b/reaction/engine/code/asm/matha.s @@ -0,0 +1,424 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// math.s +// x86 assembly-language math routines. + +#include "qasm.h" + + +#if id386 + + .data + + .align 4 +Ljmptab: .long Lcase0, Lcase1, Lcase2, Lcase3 + .long Lcase4, Lcase5, Lcase6, Lcase7 + + .text + +// TODO: rounding needed? +// stack parameter offset +#define val 4 + +.globl C(Invert24To16) +C(Invert24To16): + + movl val(%esp),%ecx + movl $0x100,%edx // 0x10000000000 as dividend + cmpl %edx,%ecx + jle LOutOfRange + + subl %eax,%eax + divl %ecx + + ret + +LOutOfRange: + movl $0xFFFFFFFF,%eax + ret + +#if 0 + +#define in 4 +#define out 8 + + .align 2 +.globl C(TransformVector) +C(TransformVector): + movl in(%esp),%eax + movl out(%esp),%edx + + flds (%eax) // in[0] + fmuls C(vright) // in[0]*vright[0] + flds (%eax) // in[0] | in[0]*vright[0] + fmuls C(vup) // in[0]*vup[0] | in[0]*vright[0] + flds (%eax) // in[0] | in[0]*vup[0] | in[0]*vright[0] + fmuls C(vpn) // in[0]*vpn[0] | in[0]*vup[0] | in[0]*vright[0] + + flds 4(%eax) // in[1] | ... + fmuls C(vright)+4 // in[1]*vright[1] | ... + flds 4(%eax) // in[1] | in[1]*vright[1] | ... + fmuls C(vup)+4 // in[1]*vup[1] | in[1]*vright[1] | ... + flds 4(%eax) // in[1] | in[1]*vup[1] | in[1]*vright[1] | ... + fmuls C(vpn)+4 // in[1]*vpn[1] | in[1]*vup[1] | in[1]*vright[1] | ... + fxch %st(2) // in[1]*vright[1] | in[1]*vup[1] | in[1]*vpn[1] | ... + + faddp %st(0),%st(5) // in[1]*vup[1] | in[1]*vpn[1] | ... + faddp %st(0),%st(3) // in[1]*vpn[1] | ... + faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum + + flds 8(%eax) // in[2] | ... + fmuls C(vright)+8 // in[2]*vright[2] | ... + flds 8(%eax) // in[2] | in[2]*vright[2] | ... + fmuls C(vup)+8 // in[2]*vup[2] | in[2]*vright[2] | ... + flds 8(%eax) // in[2] | in[2]*vup[2] | in[2]*vright[2] | ... + fmuls C(vpn)+8 // in[2]*vpn[2] | in[2]*vup[2] | in[2]*vright[2] | ... + fxch %st(2) // in[2]*vright[2] | in[2]*vup[2] | in[2]*vpn[2] | ... + + faddp %st(0),%st(5) // in[2]*vup[2] | in[2]*vpn[2] | ... + faddp %st(0),%st(3) // in[2]*vpn[2] | ... + faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum + + fstps 8(%edx) // out[2] + fstps 4(%edx) // out[1] + fstps (%edx) // out[0] + + ret + +#endif + +#define EMINS 4+4 +#define EMAXS 4+8 +#define P 4+12 + + .align 2 +.globl C(BoxOnPlaneSide) +C(BoxOnPlaneSide): + pushl %ebx + + movl P(%esp),%edx + movl EMINS(%esp),%ecx + xorl %eax,%eax + movl EMAXS(%esp),%ebx + movb pl_signbits(%edx),%al + cmpb $8,%al + jge Lerror + flds pl_normal(%edx) // p->normal[0] + fld %st(0) // p->normal[0] | p->normal[0] + // bk000422 - warning: missing prefix `*' in absolute indirect address, maybe misassembled! + // bk001129 - fix from Andrew Henderson, was: Ljmptab(,%eax,4) + jmp *Ljmptab(,%eax,4) + + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +Lcase0: + fmuls (%ebx) // p->normal[0]*emaxs[0] | p->normal[0] + flds pl_normal+4(%edx) // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0] + fxch %st(2) // p->normal[0] | p->normal[0]*emaxs[0] | + // p->normal[1] + fmuls (%ecx) // p->normal[0]*emins[0] | + // p->normal[0]*emaxs[0] | p->normal[1] + fxch %st(2) // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fld %st(0) // p->normal[1] | p->normal[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 4(%ebx) // p->normal[1]*emaxs[1] | p->normal[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + flds pl_normal+8(%edx) // p->normal[2] | p->normal[1]*emaxs[1] | + // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(2) // p->normal[1] | p->normal[1]*emaxs[1] | + // p->normal[2] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 4(%ecx) // p->normal[1]*emins[1] | + // p->normal[1]*emaxs[1] | + // p->normal[2] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(2) // p->normal[2] | p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fld %st(0) // p->normal[2] | p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 8(%ebx) // p->normal[2]*emaxs[2] | + // p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(5) // p->normal[0]*emins[0] | + // p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + faddp %st(0),%st(3) //p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + fmuls 8(%ecx) //p->normal[2]*emins[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + fxch %st(1) //p->normal[1]*emaxs[1] | + // p->normal[2]*emins[2] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + faddp %st(0),%st(3) //p->normal[2]*emins[2] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| + // p->normal[2]*emaxs[2] + fxch %st(3) //p->normal[2]*emaxs[2] + + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| + // p->normal[2]*emins[2] + faddp %st(0),%st(2) //p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // dist1 | p->normal[2]*emins[2] + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +Lcase1: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +Lcase2: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +Lcase3: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +Lcase4: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +Lcase5: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +Lcase6: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +Lcase7: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + +LSetSides: + +// sides = 0; +// if (dist1 >= p->dist) +// sides = 1; +// if (dist2 < p->dist) +// sides |= 2; + + faddp %st(0),%st(2) // dist1 | dist2 + fcomps pl_dist(%edx) + xorl %ecx,%ecx + fnstsw %ax + fcomps pl_dist(%edx) + andb $1,%ah + xorb $1,%ah + addb %ah,%cl + + fnstsw %ax + andb $1,%ah + addb %ah,%ah + addb %ah,%cl + +// return sides; + + popl %ebx + movl %ecx,%eax // return status + + ret + + +Lerror: + movl 1, %eax + ret + +#endif // id386 diff --git a/reaction/engine/code/asm/qasm.h b/reaction/engine/code/asm/qasm.h new file mode 100644 index 00000000..b02a60a2 --- /dev/null +++ b/reaction/engine/code/asm/qasm.h @@ -0,0 +1,46 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#ifndef __ASM_I386__ +#define __ASM_I386__ + +#include "../qcommon/q_platform.h" + +#ifdef __ELF__ +.section .note.GNU-stack,"",@progbits +#endif + +#ifdef __ELF__ +#define C(label) label +#else +#define C(label) _##label +#endif + +// plane_t structure +// !!! if this is changed, it must be changed in q_shared.h too !!! +#define pl_normal 0 +#define pl_dist 12 +#define pl_type 16 +#define pl_signbits 17 +#define pl_pad 18 +#define pl_size 20 + +#endif diff --git a/reaction/engine/code/asm/snapvectora.s b/reaction/engine/code/asm/snapvectora.s new file mode 100644 index 00000000..11294a05 --- /dev/null +++ b/reaction/engine/code/asm/snapvectora.s @@ -0,0 +1,103 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +// +// Sys_SnapVector NASM code (Andrew Henderson) +// See win32/win_shared.c for the Win32 equivalent +// This code is provided to ensure that the +// rounding behavior (and, if necessary, the +// precision) of DLL and QVM code are identical +// e.g. for network-visible operations. +// See ftol.nasm for operations on a single float, +// as used in compiled VM and DLL code that does +// not use this system trap. +// + +// 23/09/05 Ported to gas by intel2gas, best supporting actor Tim Angus +// + +#include "qasm.h" + +#if id386 +.data + +fpucw: .long 0 +cw037F: .long 0x037F + +.text + +// void Sys_SnapVector( float *v ) +.globl C(Sys_SnapVector) +C(Sys_SnapVector): + pushl %eax + pushl %ebp + movl %esp,%ebp + + fnstcw fpucw + movl 12(%ebp),%eax + fldcw cw037F + flds (%eax) + fistpl (%eax) + fildl (%eax) + fstps (%eax) + flds 4(%eax) + fistpl 4(%eax) + fildl 4(%eax) + fstps 4(%eax) + flds 8(%eax) + fistpl 8(%eax) + fildl 8(%eax) + fstps 8(%eax) + fldcw fpucw + + popl %ebp + popl %eax + ret + +// void Sys_SnapVectorCW( float *v, unsigned short int cw ) +.globl C(Sys_SnapVectorCW) +C(Sys_SnapVectorCW): + pushl %eax + pushl %ebp + movl %esp,%ebp + + fnstcw fpucw + movl 12(%ebp),%eax + fldcw 16(%ebp) + flds (%eax) + fistpl (%eax) + fildl (%eax) + fstps (%eax) + flds 4(%eax) + fistpl 4(%eax) + fildl 4(%eax) + fstps 4(%eax) + flds 8(%eax) + fistpl 8(%eax) + fildl 8(%eax) + fstps 8(%eax) + fldcw fpucw + + popl %ebp + popl %eax + ret +#endif diff --git a/reaction/engine/code/asm/snd_mixa.s b/reaction/engine/code/asm/snd_mixa.s new file mode 100644 index 00000000..4c6be5b7 --- /dev/null +++ b/reaction/engine/code/asm/snd_mixa.s @@ -0,0 +1,217 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// snd_mixa.s +// x86 assembly-language sound code +// + +#include "qasm.h" + +#if id386 + + .text + +#if 0 +//---------------------------------------------------------------------- +// 8-bit sound-mixing code +//---------------------------------------------------------------------- + +#define ch 4+16 +#define sc 8+16 +#define count 12+16 + +.globl C(S_PaintChannelFrom8) +C(S_PaintChannelFrom8): + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + pushl %ebp + +// int data; +// short *lscale, *rscale; +// unsigned char *sfx; +// int i; + + movl ch(%esp),%ebx + movl sc(%esp),%esi + +// if (ch->leftvol > 255) +// ch->leftvol = 255; +// if (ch->rightvol > 255) +// ch->rightvol = 255; + movl ch_leftvol(%ebx),%eax + movl ch_rightvol(%ebx),%edx + cmpl $255,%eax + jna LLeftSet + movl $255,%eax +LLeftSet: + cmpl $255,%edx + jna LRightSet + movl $255,%edx +LRightSet: + +// lscale = snd_scaletable[ch->leftvol >> 3]; +// rscale = snd_scaletable[ch->rightvol >> 3]; +// sfx = (signed char *)sc->data + ch->pos; +// ch->pos += count; + andl $0xF8,%eax + addl $20,%esi + movl (%esi),%esi + andl $0xF8,%edx + movl ch_pos(%ebx),%edi + movl count(%esp),%ecx + addl %edi,%esi + shll $7,%eax + addl %ecx,%edi + shll $7,%edx + movl %edi,ch_pos(%ebx) + addl $(C(snd_scaletable)),%eax + addl $(C(snd_scaletable)),%edx + subl %ebx,%ebx + movb -1(%esi,%ecx,1),%bl + + testl $1,%ecx + jz LMix8Loop + + movl (%eax,%ebx,4),%edi + movl (%edx,%ebx,4),%ebp + addl C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size),%edi + addl C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size),%ebp + movl %edi,C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size) + movl %ebp,C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size) + movb -2(%esi,%ecx,1),%bl + + decl %ecx + jz LDone + +// for (i=0 ; i>8; +// if (val > 0x7fff) +// snd_out[i] = 0x7fff; +// else if (val < (short)0x8000) +// snd_out[i] = (short)0x8000; +// else +// snd_out[i] = val; + movl -8(%ebx,%ecx,4),%eax + sarl $8,%eax + cmpl $0x7FFF,%eax + jg LClampHigh + cmpl $0xFFFF8000,%eax + jnl LClampDone + movl $0xFFFF8000,%eax + jmp LClampDone +LClampHigh: + movl $0x7FFF,%eax +LClampDone: + +// val = (snd_p[i+1]*snd_vol)>>8; +// if (val > 0x7fff) +// snd_out[i+1] = 0x7fff; +// else if (val < (short)0x8000) +// snd_out[i+1] = (short)0x8000; +// else +// snd_out[i+1] = val; + movl -4(%ebx,%ecx,4),%edx + sarl $8,%edx + cmpl $0x7FFF,%edx + jg LClampHigh2 + cmpl $0xFFFF8000,%edx + jnl LClampDone2 + movl $0xFFFF8000,%edx + jmp LClampDone2 +LClampHigh2: + movl $0x7FFF,%edx +LClampDone2: + shll $16,%edx + andl $0xFFFF,%eax + orl %eax,%edx + movl %edx,-4(%edi,%ecx,2) + +// } + subl $2,%ecx + jnz LWLBLoopTop + +// snd_p += snd_linear_count; + + popl %ebx + popl %edi + + ret + +#endif // id386 + diff --git a/reaction/engine/code/botlib/aasfile.h b/reaction/engine/code/botlib/aasfile.h new file mode 100644 index 00000000..8f2fbf62 --- /dev/null +++ b/reaction/engine/code/botlib/aasfile.h @@ -0,0 +1,267 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + + +//NOTE: int = default signed +// default long + +#define AASID (('S'<<24)+('A'<<16)+('A'<<8)+'E') +#define AASVERSION_OLD 4 +#define AASVERSION 5 + +//presence types +#define PRESENCE_NONE 1 +#define PRESENCE_NORMAL 2 +#define PRESENCE_CROUCH 4 + +//travel types +#define MAX_TRAVELTYPES 32 +#define TRAVEL_INVALID 1 //temporary not possible +#define TRAVEL_WALK 2 //walking +#define TRAVEL_CROUCH 3 //crouching +#define TRAVEL_BARRIERJUMP 4 //jumping onto a barrier +#define TRAVEL_JUMP 5 //jumping +#define TRAVEL_LADDER 6 //climbing a ladder +#define TRAVEL_WALKOFFLEDGE 7 //walking of a ledge +#define TRAVEL_SWIM 8 //swimming +#define TRAVEL_WATERJUMP 9 //jump out of the water +#define TRAVEL_TELEPORT 10 //teleportation +#define TRAVEL_ELEVATOR 11 //travel by elevator +#define TRAVEL_ROCKETJUMP 12 //rocket jumping required for travel +#define TRAVEL_BFGJUMP 13 //bfg jumping required for travel +#define TRAVEL_GRAPPLEHOOK 14 //grappling hook required for travel +#define TRAVEL_DOUBLEJUMP 15 //double jump +#define TRAVEL_RAMPJUMP 16 //ramp jump +#define TRAVEL_STRAFEJUMP 17 //strafe jump +#define TRAVEL_JUMPPAD 18 //jump pad +#define TRAVEL_FUNCBOB 19 //func bob + +//additional travel flags +#define TRAVELTYPE_MASK 0xFFFFFF +#define TRAVELFLAG_NOTTEAM1 (1 << 24) +#define TRAVELFLAG_NOTTEAM2 (2 << 24) + +//face flags +#define FACE_SOLID 1 //just solid at the other side +#define FACE_LADDER 2 //ladder +#define FACE_GROUND 4 //standing on ground when in this face +#define FACE_GAP 8 //gap in the ground +#define FACE_LIQUID 16 //face seperating two areas with liquid +#define FACE_LIQUIDSURFACE 32 //face seperating liquid and air +#define FACE_BRIDGE 64 //can walk over this face if bridge is closed + +//area contents +#define AREACONTENTS_WATER 1 +#define AREACONTENTS_LAVA 2 +#define AREACONTENTS_SLIME 4 +#define AREACONTENTS_CLUSTERPORTAL 8 +#define AREACONTENTS_TELEPORTAL 16 +#define AREACONTENTS_ROUTEPORTAL 32 +#define AREACONTENTS_TELEPORTER 64 +#define AREACONTENTS_JUMPPAD 128 +#define AREACONTENTS_DONOTENTER 256 +#define AREACONTENTS_VIEWPORTAL 512 +#define AREACONTENTS_MOVER 1024 +#define AREACONTENTS_NOTTEAM1 2048 +#define AREACONTENTS_NOTTEAM2 4096 +//number of model of the mover inside this area +#define AREACONTENTS_MODELNUMSHIFT 24 +#define AREACONTENTS_MAXMODELNUM 0xFF +#define AREACONTENTS_MODELNUM (AREACONTENTS_MAXMODELNUM << AREACONTENTS_MODELNUMSHIFT) + +//area flags +#define AREA_GROUNDED 1 //bot can stand on the ground +#define AREA_LADDER 2 //area contains one or more ladder faces +#define AREA_LIQUID 4 //area contains a liquid +#define AREA_DISABLED 8 //area is disabled for routing when set +#define AREA_BRIDGE 16 //area ontop of a bridge + +//aas file header lumps +#define AAS_LUMPS 14 +#define AASLUMP_BBOXES 0 +#define AASLUMP_VERTEXES 1 +#define AASLUMP_PLANES 2 +#define AASLUMP_EDGES 3 +#define AASLUMP_EDGEINDEX 4 +#define AASLUMP_FACES 5 +#define AASLUMP_FACEINDEX 6 +#define AASLUMP_AREAS 7 +#define AASLUMP_AREASETTINGS 8 +#define AASLUMP_REACHABILITY 9 +#define AASLUMP_NODES 10 +#define AASLUMP_PORTALS 11 +#define AASLUMP_PORTALINDEX 12 +#define AASLUMP_CLUSTERS 13 + +//========== bounding box ========= + +//bounding box +typedef struct aas_bbox_s +{ + int presencetype; + int flags; + vec3_t mins, maxs; +} aas_bbox_t; + +//============ settings =========== + +//reachability to another area +typedef struct aas_reachability_s +{ + int areanum; //number of the reachable area + int facenum; //number of the face towards the other area + int edgenum; //number of the edge towards the other area + vec3_t start; //start point of inter area movement + vec3_t end; //end point of inter area movement + int traveltype; //type of travel required to get to the area + unsigned short int traveltime;//travel time of the inter area movement +} aas_reachability_t; + +//area settings +typedef struct aas_areasettings_s +{ + //could also add all kind of statistic fields + int contents; //contents of the area + int areaflags; //several area flags + int presencetype; //how a bot can be present in this area + int cluster; //cluster the area belongs to, if negative it's a portal + int clusterareanum; //number of the area in the cluster + int numreachableareas; //number of reachable areas from this one + int firstreachablearea; //first reachable area in the reachable area index +} aas_areasettings_t; + +//cluster portal +typedef struct aas_portal_s +{ + int areanum; //area that is the actual portal + int frontcluster; //cluster at front of portal + int backcluster; //cluster at back of portal + int clusterareanum[2]; //number of the area in the front and back cluster +} aas_portal_t; + +//cluster portal index +typedef int aas_portalindex_t; + +//cluster +typedef struct aas_cluster_s +{ + int numareas; //number of areas in the cluster + int numreachabilityareas; //number of areas with reachabilities + int numportals; //number of cluster portals + int firstportal; //first cluster portal in the index +} aas_cluster_t; + +//============ 3d definition ============ + +typedef vec3_t aas_vertex_t; + +//just a plane in the third dimension +typedef struct aas_plane_s +{ + vec3_t normal; //normal vector of the plane + float dist; //distance of the plane (normal vector * distance = point in plane) + int type; +} aas_plane_t; + +//edge +typedef struct aas_edge_s +{ + int v[2]; //numbers of the vertexes of this edge +} aas_edge_t; + +//edge index, negative if vertexes are reversed +typedef int aas_edgeindex_t; + +//a face bounds an area, often it will also seperate two areas +typedef struct aas_face_s +{ + int planenum; //number of the plane this face is in + int faceflags; //face flags (no use to create face settings for just this field) + int numedges; //number of edges in the boundary of the face + int firstedge; //first edge in the edge index + int frontarea; //area at the front of this face + int backarea; //area at the back of this face +} aas_face_t; + +//face index, stores a negative index if backside of face +typedef int aas_faceindex_t; + +//area with a boundary of faces +typedef struct aas_area_s +{ + int areanum; //number of this area + //3d definition + int numfaces; //number of faces used for the boundary of the area + int firstface; //first face in the face index used for the boundary of the area + vec3_t mins; //mins of the area + vec3_t maxs; //maxs of the area + vec3_t center; //'center' of the area +} aas_area_t; + +//nodes of the bsp tree +typedef struct aas_node_s +{ + int planenum; + int children[2]; //child nodes of this node, or areas as leaves when negative + //when a child is zero it's a solid leaf +} aas_node_t; + +//=========== aas file =============== + +//header lump +typedef struct +{ + int fileofs; + int filelen; +} aas_lump_t; + +//aas file header +typedef struct aas_header_s +{ + int ident; + int version; + int bspchecksum; + //data entries + aas_lump_t lumps[AAS_LUMPS]; +} aas_header_t; + + +//====== additional information ====== +/* + +- when a node child is a solid leaf the node child number is zero +- two adjacent areas (sharing a plane at opposite sides) share a face + this face is a portal between the areas +- when an area uses a face from the faceindex with a positive index + then the face plane normal points into the area +- the face edges are stored counter clockwise using the edgeindex +- two adjacent convex areas (sharing a face) only share One face + this is a simple result of the areas being convex +- the areas can't have a mixture of ground and gap faces + other mixtures of faces in one area are allowed +- areas with the AREACONTENTS_CLUSTERPORTAL in the settings have + the cluster number set to the negative portal number +- edge zero is a dummy +- face zero is a dummy +- area zero is a dummy +- node zero is a dummy +*/ diff --git a/reaction/engine/code/botlib/be_aas.h b/reaction/engine/code/botlib/be_aas.h new file mode 100644 index 00000000..dbf24bc1 --- /dev/null +++ b/reaction/engine/code/botlib/be_aas.h @@ -0,0 +1,221 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: be_aas.h + * + * desc: Area Awareness System, stuff exported to the AI + * + * $Archive: /source/code/botlib/be_aas.h $ + * + *****************************************************************************/ + +#ifndef MAX_STRINGFIELD +#define MAX_STRINGFIELD 80 +#endif + +//travel flags +#define TFL_INVALID 0x00000001 //traveling temporary not possible +#define TFL_WALK 0x00000002 //walking +#define TFL_CROUCH 0x00000004 //crouching +#define TFL_BARRIERJUMP 0x00000008 //jumping onto a barrier +#define TFL_JUMP 0x00000010 //jumping +#define TFL_LADDER 0x00000020 //climbing a ladder +#define TFL_WALKOFFLEDGE 0x00000080 //walking of a ledge +#define TFL_SWIM 0x00000100 //swimming +#define TFL_WATERJUMP 0x00000200 //jumping out of the water +#define TFL_TELEPORT 0x00000400 //teleporting +#define TFL_ELEVATOR 0x00000800 //elevator +#define TFL_ROCKETJUMP 0x00001000 //rocket jumping +#define TFL_BFGJUMP 0x00002000 //bfg jumping +#define TFL_GRAPPLEHOOK 0x00004000 //grappling hook +#define TFL_DOUBLEJUMP 0x00008000 //double jump +#define TFL_RAMPJUMP 0x00010000 //ramp jump +#define TFL_STRAFEJUMP 0x00020000 //strafe jump +#define TFL_JUMPPAD 0x00040000 //jump pad +#define TFL_AIR 0x00080000 //travel through air +#define TFL_WATER 0x00100000 //travel through water +#define TFL_SLIME 0x00200000 //travel through slime +#define TFL_LAVA 0x00400000 //travel through lava +#define TFL_DONOTENTER 0x00800000 //travel through donotenter area +#define TFL_FUNCBOB 0x01000000 //func bobbing +#define TFL_FLIGHT 0x02000000 //flight +#define TFL_BRIDGE 0x04000000 //move over a bridge +// +#define TFL_NOTTEAM1 0x08000000 //not team 1 +#define TFL_NOTTEAM2 0x10000000 //not team 2 + +//default travel flags +#define TFL_DEFAULT TFL_WALK|TFL_CROUCH|TFL_BARRIERJUMP|\ + TFL_JUMP|TFL_LADDER|\ + TFL_WALKOFFLEDGE|TFL_SWIM|TFL_WATERJUMP|\ + TFL_TELEPORT|TFL_ELEVATOR|\ + TFL_AIR|TFL_WATER|TFL_JUMPPAD|TFL_FUNCBOB + +typedef enum +{ + SOLID_NOT, // no interaction with other objects + SOLID_TRIGGER, // only touch when inside, after moving + SOLID_BBOX, // touch on edge + SOLID_BSP // bsp clip, touch on edge +} solid_t; + +//a trace is returned when a box is swept through the AAS world +typedef struct aas_trace_s +{ + qboolean startsolid; // if true, the initial point was in a solid area + float fraction; // time completed, 1.0 = didn't hit anything + vec3_t endpos; // final position + int ent; // entity blocking the trace + int lastarea; // last area the trace was in (zero if none) + int area; // area blocking the trace (zero if none) + int planenum; // number of the plane that was hit +} aas_trace_t; + +/* Defined in botlib.h + +//bsp_trace_t hit surface +typedef struct bsp_surface_s +{ + char name[16]; + int flags; + int value; +} bsp_surface_t; + +//a trace is returned when a box is swept through the BSP world +typedef struct bsp_trace_s +{ + qboolean allsolid; // if true, plane is not valid + qboolean startsolid; // if true, the initial point was in a solid area + float fraction; // time completed, 1.0 = didn't hit anything + vec3_t endpos; // final position + cplane_t plane; // surface normal at impact + float exp_dist; // expanded plane distance + int sidenum; // number of the brush side hit + bsp_surface_t surface; // hit surface + int contents; // contents on other side of surface hit + int ent; // number of entity hit +} bsp_trace_t; +// +*/ + +//entity info +typedef struct aas_entityinfo_s +{ + int valid; // true if updated this frame + int type; // entity type + int flags; // entity flags + float ltime; // local time + float update_time; // time between last and current update + int number; // number of the entity + vec3_t origin; // origin of the entity + vec3_t angles; // angles of the model + vec3_t old_origin; // for lerping + vec3_t lastvisorigin; // last visible origin + vec3_t mins; // bounding box minimums + vec3_t maxs; // bounding box maximums + int groundent; // ground entity + int solid; // solid type + int modelindex; // model used + int modelindex2; // weapons, CTF flags, etc + int frame; // model frame number + int event; // impulse events -- muzzle flashes, footsteps, etc + int eventParm; // even parameter + int powerups; // bit flags + int weapon; // determines weapon and flash model, etc + int legsAnim; // mask off ANIM_TOGGLEBIT + int torsoAnim; // mask off ANIM_TOGGLEBIT +} aas_entityinfo_t; + +// area info +typedef struct aas_areainfo_s +{ + int contents; + int flags; + int presencetype; + int cluster; + vec3_t mins; + vec3_t maxs; + vec3_t center; +} aas_areainfo_t; + +// client movement prediction stop events, stop as soon as: +#define SE_NONE 0 +#define SE_HITGROUND 1 // the ground is hit +#define SE_LEAVEGROUND 2 // there's no ground +#define SE_ENTERWATER 4 // water is entered +#define SE_ENTERSLIME 8 // slime is entered +#define SE_ENTERLAVA 16 // lava is entered +#define SE_HITGROUNDDAMAGE 32 // the ground is hit with damage +#define SE_GAP 64 // there's a gap +#define SE_TOUCHJUMPPAD 128 // touching a jump pad area +#define SE_TOUCHTELEPORTER 256 // touching teleporter +#define SE_ENTERAREA 512 // the given stoparea is entered +#define SE_HITGROUNDAREA 1024 // a ground face in the area is hit +#define SE_HITBOUNDINGBOX 2048 // hit the specified bounding box +#define SE_TOUCHCLUSTERPORTAL 4096 // touching a cluster portal + +typedef struct aas_clientmove_s +{ + vec3_t endpos; //position at the end of movement prediction + int endarea; //area at end of movement prediction + vec3_t velocity; //velocity at the end of movement prediction + aas_trace_t trace; //last trace + int presencetype; //presence type at end of movement prediction + int stopevent; //event that made the prediction stop + int endcontents; //contents at the end of movement prediction + float time; //time predicted ahead + int frames; //number of frames predicted ahead +} aas_clientmove_t; + +// alternate route goals +#define ALTROUTEGOAL_ALL 1 +#define ALTROUTEGOAL_CLUSTERPORTALS 2 +#define ALTROUTEGOAL_VIEWPORTALS 4 + +typedef struct aas_altroutegoal_s +{ + vec3_t origin; + int areanum; + unsigned short starttraveltime; + unsigned short goaltraveltime; + unsigned short extratraveltime; +} aas_altroutegoal_t; + +// route prediction stop events +#define RSE_NONE 0 +#define RSE_NOROUTE 1 //no route to goal +#define RSE_USETRAVELTYPE 2 //stop as soon as on of the given travel types is used +#define RSE_ENTERCONTENTS 4 //stop when entering the given contents +#define RSE_ENTERAREA 8 //stop when entering the given area + +typedef struct aas_predictroute_s +{ + vec3_t endpos; //position at the end of movement prediction + int endarea; //area at end of movement prediction + int stopevent; //event that made the prediction stop + int endcontents; //contents at the end of movement prediction + int endtravelflags; //end travel flags + int numareas; //number of areas predicted ahead + int time; //time predicted ahead (in hundreth of a sec) +} aas_predictroute_t; diff --git a/reaction/engine/code/botlib/be_aas_bsp.h b/reaction/engine/code/botlib/be_aas_bsp.h new file mode 100644 index 00000000..932874a1 --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_bsp.h @@ -0,0 +1,89 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_bsp.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_bsp.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +//loads the given BSP file +int AAS_LoadBSPFile(void); +//dump the loaded BSP data +void AAS_DumpBSPData(void); +//unlink the given entity from the bsp tree leaves +void AAS_UnlinkFromBSPLeaves(bsp_link_t *leaves); +//link the given entity to the bsp tree leaves of the given model +bsp_link_t *AAS_BSPLinkEntity(vec3_t absmins, + vec3_t absmaxs, + int entnum, + int modelnum); + +//calculates collision with given entity +qboolean AAS_EntityCollision(int entnum, + vec3_t start, + vec3_t boxmins, + vec3_t boxmaxs, + vec3_t end, + int contentmask, + bsp_trace_t *trace); +//for debugging +void AAS_PrintFreeBSPLinks(char *str); +// +#endif //AASINTERN + +#define MAX_EPAIRKEY 128 + +//trace through the world +bsp_trace_t AAS_Trace( vec3_t start, + vec3_t mins, + vec3_t maxs, + vec3_t end, + int passent, + int contentmask); +//returns the contents at the given point +int AAS_PointContents(vec3_t point); +//returns true when p2 is in the PVS of p1 +qboolean AAS_inPVS(vec3_t p1, vec3_t p2); +//returns true when p2 is in the PHS of p1 +qboolean AAS_inPHS(vec3_t p1, vec3_t p2); +//returns true if the given areas are connected +qboolean AAS_AreasConnected(int area1, int area2); +//creates a list with entities totally or partly within the given box +int AAS_BoxEntities(vec3_t absmins, vec3_t absmaxs, int *list, int maxcount); +//gets the mins, maxs and origin of a BSP model +void AAS_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin); +//handle to the next bsp entity +int AAS_NextBSPEntity(int ent); +//return the value of the BSP epair key +int AAS_ValueForBSPEpairKey(int ent, char *key, char *value, int size); +//get a vector for the BSP epair key +int AAS_VectorForBSPEpairKey(int ent, char *key, vec3_t v); +//get a float for the BSP epair key +int AAS_FloatForBSPEpairKey(int ent, char *key, float *value); +//get an integer for the BSP epair key +int AAS_IntForBSPEpairKey(int ent, char *key, int *value); + diff --git a/reaction/engine/code/botlib/be_aas_bspq3.c b/reaction/engine/code/botlib/be_aas_bspq3.c new file mode 100644 index 00000000..9bfc824f --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_bspq3.c @@ -0,0 +1,487 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_bspq3.c + * + * desc: BSP, Environment Sampling + * + * $Archive: /MissionPack/code/botlib/be_aas_bspq3.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_aas_def.h" + +extern botlib_import_t botimport; + +//#define TRACE_DEBUG + +#define ON_EPSILON 0.005 +//#define DEG2RAD( a ) (( a * M_PI ) / 180.0F) + +#define MAX_BSPENTITIES 2048 + +typedef struct rgb_s +{ + int red; + int green; + int blue; +} rgb_t; + +//bsp entity epair +typedef struct bsp_epair_s +{ + char *key; + char *value; + struct bsp_epair_s *next; +} bsp_epair_t; + +//bsp data entity +typedef struct bsp_entity_s +{ + bsp_epair_t *epairs; +} bsp_entity_t; + +//id Sofware BSP data +typedef struct bsp_s +{ + //true when bsp file is loaded + int loaded; + //entity data + int entdatasize; + char *dentdata; + //bsp entities + int numentities; + bsp_entity_t entities[MAX_BSPENTITIES]; +} bsp_t; + +//global bsp +bsp_t bspworld; + + +#ifdef BSP_DEBUG +typedef struct cname_s +{ + int value; + char *name; +} cname_t; + +cname_t contentnames[] = +{ + {CONTENTS_SOLID,"CONTENTS_SOLID"}, + {CONTENTS_WINDOW,"CONTENTS_WINDOW"}, + {CONTENTS_AUX,"CONTENTS_AUX"}, + {CONTENTS_LAVA,"CONTENTS_LAVA"}, + {CONTENTS_SLIME,"CONTENTS_SLIME"}, + {CONTENTS_WATER,"CONTENTS_WATER"}, + {CONTENTS_MIST,"CONTENTS_MIST"}, + {LAST_VISIBLE_CONTENTS,"LAST_VISIBLE_CONTENTS"}, + + {CONTENTS_AREAPORTAL,"CONTENTS_AREAPORTAL"}, + {CONTENTS_PLAYERCLIP,"CONTENTS_PLAYERCLIP"}, + {CONTENTS_MONSTERCLIP,"CONTENTS_MONSTERCLIP"}, + {CONTENTS_CURRENT_0,"CONTENTS_CURRENT_0"}, + {CONTENTS_CURRENT_90,"CONTENTS_CURRENT_90"}, + {CONTENTS_CURRENT_180,"CONTENTS_CURRENT_180"}, + {CONTENTS_CURRENT_270,"CONTENTS_CURRENT_270"}, + {CONTENTS_CURRENT_UP,"CONTENTS_CURRENT_UP"}, + {CONTENTS_CURRENT_DOWN,"CONTENTS_CURRENT_DOWN"}, + {CONTENTS_ORIGIN,"CONTENTS_ORIGIN"}, + {CONTENTS_MONSTER,"CONTENTS_MONSTER"}, + {CONTENTS_DEADMONSTER,"CONTENTS_DEADMONSTER"}, + {CONTENTS_DETAIL,"CONTENTS_DETAIL"}, + {CONTENTS_TRANSLUCENT,"CONTENTS_TRANSLUCENT"}, + {CONTENTS_LADDER,"CONTENTS_LADDER"}, + {0, 0} +}; + +void PrintContents(int contents) +{ + int i; + + for (i = 0; contentnames[i].value; i++) + { + if (contents & contentnames[i].value) + { + botimport.Print(PRT_MESSAGE, "%s\n", contentnames[i].name); + } //end if + } //end for +} //end of the function PrintContents + +#endif // BSP_DEBUG +//=========================================================================== +// traces axial boxes of any size through the world +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bsp_trace_t AAS_Trace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) +{ + bsp_trace_t bsptrace; + botimport.Trace(&bsptrace, start, mins, maxs, end, passent, contentmask); + return bsptrace; +} //end of the function AAS_Trace +//=========================================================================== +// returns the contents at the given point +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PointContents(vec3_t point) +{ + return botimport.PointContents(point); +} //end of the function AAS_PointContents +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_EntityCollision(int entnum, + vec3_t start, vec3_t boxmins, vec3_t boxmaxs, vec3_t end, + int contentmask, bsp_trace_t *trace) +{ + bsp_trace_t enttrace; + + botimport.EntityTrace(&enttrace, start, boxmins, boxmaxs, end, entnum, contentmask); + if (enttrace.fraction < trace->fraction) + { + Com_Memcpy(trace, &enttrace, sizeof(bsp_trace_t)); + return qtrue; + } //end if + return qfalse; +} //end of the function AAS_EntityCollision +//=========================================================================== +// returns true if in Potentially Hearable Set +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_inPVS(vec3_t p1, vec3_t p2) +{ + return botimport.inPVS(p1, p2); +} //end of the function AAS_InPVS +//=========================================================================== +// returns true if in Potentially Visible Set +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_inPHS(vec3_t p1, vec3_t p2) +{ + return qtrue; +} //end of the function AAS_inPHS +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin) +{ + botimport.BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); +} //end of the function AAS_BSPModelMinsMaxs +//=========================================================================== +// unlinks the entity from all leaves +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_UnlinkFromBSPLeaves(bsp_link_t *leaves) +{ +} //end of the function AAS_UnlinkFromBSPLeaves +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bsp_link_t *AAS_BSPLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum, int modelnum) +{ + return NULL; +} //end of the function AAS_BSPLinkEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BoxEntities(vec3_t absmins, vec3_t absmaxs, int *list, int maxcount) +{ + return 0; +} //end of the function AAS_BoxEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NextBSPEntity(int ent) +{ + ent++; + if (ent >= 1 && ent < bspworld.numentities) return ent; + return 0; +} //end of the function AAS_NextBSPEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BSPEntityInRange(int ent) +{ + if (ent <= 0 || ent >= bspworld.numentities) + { + botimport.Print(PRT_MESSAGE, "bsp entity out of range\n"); + return qfalse; + } //end if + return qtrue; +} //end of the function AAS_BSPEntityInRange +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_ValueForBSPEpairKey(int ent, char *key, char *value, int size) +{ + bsp_epair_t *epair; + + value[0] = '\0'; + if (!AAS_BSPEntityInRange(ent)) return qfalse; + for (epair = bspworld.entities[ent].epairs; epair; epair = epair->next) + { + if (!strcmp(epair->key, key)) + { + strncpy(value, epair->value, size-1); + value[size-1] = '\0'; + return qtrue; + } //end if + } //end for + return qfalse; +} //end of the function AAS_FindBSPEpair +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_VectorForBSPEpairKey(int ent, char *key, vec3_t v) +{ + char buf[MAX_EPAIRKEY]; + double v1, v2, v3; + + VectorClear(v); + if (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse; + //scanf into doubles, then assign, so it is vec_t size independent + v1 = v2 = v3 = 0; + sscanf(buf, "%lf %lf %lf", &v1, &v2, &v3); + v[0] = v1; + v[1] = v2; + v[2] = v3; + return qtrue; +} //end of the function AAS_VectorForBSPEpairKey +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_FloatForBSPEpairKey(int ent, char *key, float *value) +{ + char buf[MAX_EPAIRKEY]; + + *value = 0; + if (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse; + *value = atof(buf); + return qtrue; +} //end of the function AAS_FloatForBSPEpairKey +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_IntForBSPEpairKey(int ent, char *key, int *value) +{ + char buf[MAX_EPAIRKEY]; + + *value = 0; + if (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse; + *value = atoi(buf); + return qtrue; +} //end of the function AAS_IntForBSPEpairKey +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeBSPEntities(void) +{ + int i; + bsp_entity_t *ent; + bsp_epair_t *epair, *nextepair; + + for (i = 1; i < bspworld.numentities; i++) + { + ent = &bspworld.entities[i]; + for (epair = ent->epairs; epair; epair = nextepair) + { + nextepair = epair->next; + // + if (epair->key) FreeMemory(epair->key); + if (epair->value) FreeMemory(epair->value); + FreeMemory(epair); + } //end for + } //end for + bspworld.numentities = 0; +} //end of the function AAS_FreeBSPEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ParseBSPEntities(void) +{ + script_t *script; + token_t token; + bsp_entity_t *ent; + bsp_epair_t *epair; + + script = LoadScriptMemory(bspworld.dentdata, bspworld.entdatasize, "entdata"); + SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES|SCFL_NOSTRINGESCAPECHARS);//SCFL_PRIMITIVE); + + bspworld.numentities = 1; + + while(PS_ReadToken(script, &token)) + { + if (strcmp(token.string, "{")) + { + ScriptError(script, "invalid %s\n", token.string); + AAS_FreeBSPEntities(); + FreeScript(script); + return; + } //end if + if (bspworld.numentities >= MAX_BSPENTITIES) + { + botimport.Print(PRT_MESSAGE, "too many entities in BSP file\n"); + break; + } //end if + ent = &bspworld.entities[bspworld.numentities]; + bspworld.numentities++; + ent->epairs = NULL; + while(PS_ReadToken(script, &token)) + { + if (!strcmp(token.string, "}")) break; + epair = (bsp_epair_t *) GetClearedHunkMemory(sizeof(bsp_epair_t)); + epair->next = ent->epairs; + ent->epairs = epair; + if (token.type != TT_STRING) + { + ScriptError(script, "invalid %s\n", token.string); + AAS_FreeBSPEntities(); + FreeScript(script); + return; + } //end if + StripDoubleQuotes(token.string); + epair->key = (char *) GetHunkMemory(strlen(token.string) + 1); + strcpy(epair->key, token.string); + if (!PS_ExpectTokenType(script, TT_STRING, 0, &token)) + { + AAS_FreeBSPEntities(); + FreeScript(script); + return; + } //end if + StripDoubleQuotes(token.string); + epair->value = (char *) GetHunkMemory(strlen(token.string) + 1); + strcpy(epair->value, token.string); + } //end while + if (strcmp(token.string, "}")) + { + ScriptError(script, "missing }\n"); + AAS_FreeBSPEntities(); + FreeScript(script); + return; + } //end if + } //end while + FreeScript(script); +} //end of the function AAS_ParseBSPEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BSPTraceLight(vec3_t start, vec3_t end, vec3_t endpos, int *red, int *green, int *blue) +{ + return 0; +} //end of the function AAS_BSPTraceLight +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DumpBSPData(void) +{ + AAS_FreeBSPEntities(); + + if (bspworld.dentdata) FreeMemory(bspworld.dentdata); + bspworld.dentdata = NULL; + bspworld.entdatasize = 0; + // + bspworld.loaded = qfalse; + Com_Memset( &bspworld, 0, sizeof(bspworld) ); +} //end of the function AAS_DumpBSPData +//=========================================================================== +// load an bsp file +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_LoadBSPFile(void) +{ + AAS_DumpBSPData(); + bspworld.entdatasize = strlen(botimport.BSPEntityData()) + 1; + bspworld.dentdata = (char *) GetClearedHunkMemory(bspworld.entdatasize); + Com_Memcpy(bspworld.dentdata, botimport.BSPEntityData(), bspworld.entdatasize); + AAS_ParseBSPEntities(); + bspworld.loaded = qtrue; + return BLERR_NOERROR; +} //end of the function AAS_LoadBSPFile diff --git a/reaction/engine/code/botlib/be_aas_cluster.c b/reaction/engine/code/botlib/be_aas_cluster.c new file mode 100644 index 00000000..6d577fa8 --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_cluster.c @@ -0,0 +1,1545 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_cluster.c + * + * desc: area clustering + * + * $Archive: /MissionPack/code/botlib/be_aas_cluster.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_log.h" +#include "l_memory.h" +#include "l_libvar.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_aas_def.h" + +extern botlib_import_t botimport; + +#define AAS_MAX_PORTALS 65536 +#define AAS_MAX_PORTALINDEXSIZE 65536 +#define AAS_MAX_CLUSTERS 65536 +// +#define MAX_PORTALAREAS 1024 + +// do not flood through area faces, only use reachabilities +int nofaceflood = qtrue; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveClusterAreas(void) +{ + int i; + + for (i = 1; i < aasworld.numareas; i++) + { + aasworld.areasettings[i].cluster = 0; + } //end for +} //end of the function AAS_RemoveClusterAreas +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ClearCluster(int clusternum) +{ + int i; + + for (i = 1; i < aasworld.numareas; i++) + { + if (aasworld.areasettings[i].cluster == clusternum) + { + aasworld.areasettings[i].cluster = 0; + } //end if + } //end for +} //end of the function AAS_ClearCluster +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemovePortalsClusterReference(int clusternum) +{ + int portalnum; + + for (portalnum = 1; portalnum < aasworld.numportals; portalnum++) + { + if (aasworld.portals[portalnum].frontcluster == clusternum) + { + aasworld.portals[portalnum].frontcluster = 0; + } //end if + if (aasworld.portals[portalnum].backcluster == clusternum) + { + aasworld.portals[portalnum].backcluster = 0; + } //end if + } //end for +} //end of the function AAS_RemovePortalsClusterReference +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_UpdatePortal(int areanum, int clusternum) +{ + int portalnum; + aas_portal_t *portal; + aas_cluster_t *cluster; + + //find the portal of the area + for (portalnum = 1; portalnum < aasworld.numportals; portalnum++) + { + if (aasworld.portals[portalnum].areanum == areanum) break; + } //end for + // + if (portalnum == aasworld.numportals) + { + AAS_Error("no portal of area %d", areanum); + return qtrue; + } //end if + // + portal = &aasworld.portals[portalnum]; + //if the portal is already fully updated + if (portal->frontcluster == clusternum) return qtrue; + if (portal->backcluster == clusternum) return qtrue; + //if the portal has no front cluster yet + if (!portal->frontcluster) + { + portal->frontcluster = clusternum; + } //end if + //if the portal has no back cluster yet + else if (!portal->backcluster) + { + portal->backcluster = clusternum; + } //end else if + else + { + //remove the cluster portal flag contents + aasworld.areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; + Log_Write("portal area %d is seperating more than two clusters\r\n", areanum); + return qfalse; + } //end else + if (aasworld.portalindexsize >= AAS_MAX_PORTALINDEXSIZE) + { + AAS_Error("AAS_MAX_PORTALINDEXSIZE"); + return qtrue; + } //end if + //set the area cluster number to the negative portal number + aasworld.areasettings[areanum].cluster = -portalnum; + //add the portal to the cluster using the portal index + cluster = &aasworld.clusters[clusternum]; + aasworld.portalindex[cluster->firstportal + cluster->numportals] = portalnum; + aasworld.portalindexsize++; + cluster->numportals++; + return qtrue; +} //end of the function AAS_UpdatePortal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_FloodClusterAreas_r(int areanum, int clusternum) +{ + aas_area_t *area; + aas_face_t *face; + int facenum, i; + + // + if (areanum <= 0 || areanum >= aasworld.numareas) + { + AAS_Error("AAS_FloodClusterAreas_r: areanum out of range"); + return qfalse; + } //end if + //if the area is already part of a cluster + if (aasworld.areasettings[areanum].cluster > 0) + { + if (aasworld.areasettings[areanum].cluster == clusternum) return qtrue; + // + //there's a reachability going from one cluster to another only in one direction + // + AAS_Error("cluster %d touched cluster %d at area %d\r\n", + clusternum, aasworld.areasettings[areanum].cluster, areanum); + return qfalse; + } //end if + //don't add the cluster portal areas to the clusters + if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) + { + return AAS_UpdatePortal(areanum, clusternum); + } //end if + //set the area cluster number + aasworld.areasettings[areanum].cluster = clusternum; + aasworld.areasettings[areanum].clusterareanum = + aasworld.clusters[clusternum].numareas; + //the cluster has an extra area + aasworld.clusters[clusternum].numareas++; + + area = &aasworld.areas[areanum]; + //use area faces to flood into adjacent areas + if (!nofaceflood) + { + for (i = 0; i < area->numfaces; i++) + { + facenum = abs(aasworld.faceindex[area->firstface + i]); + face = &aasworld.faces[facenum]; + if (face->frontarea == areanum) + { + if (face->backarea) if (!AAS_FloodClusterAreas_r(face->backarea, clusternum)) return qfalse; + } //end if + else + { + if (face->frontarea) if (!AAS_FloodClusterAreas_r(face->frontarea, clusternum)) return qfalse; + } //end else + } //end for + } //end if + //use the reachabilities to flood into other areas + for (i = 0; i < aasworld.areasettings[areanum].numreachableareas; i++) + { + if (!aasworld.reachability[ + aasworld.areasettings[areanum].firstreachablearea + i].areanum) + { + continue; + } //end if + if (!AAS_FloodClusterAreas_r(aasworld.reachability[ + aasworld.areasettings[areanum].firstreachablearea + i].areanum, clusternum)) return qfalse; + } //end for + return qtrue; +} //end of the function AAS_FloodClusterAreas_r +//=========================================================================== +// try to flood from all areas without cluster into areas with a cluster set +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_FloodClusterAreasUsingReachabilities(int clusternum) +{ + int i, j, areanum; + + for (i = 1; i < aasworld.numareas; i++) + { + //if this area already has a cluster set + if (aasworld.areasettings[i].cluster) + continue; + //if this area is a cluster portal + if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) + continue; + //loop over the reachable areas from this area + for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) + { + //the reachable area + areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; + //if this area is a cluster portal + if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) + continue; + //if this area has a cluster set + if (aasworld.areasettings[areanum].cluster) + { + if (!AAS_FloodClusterAreas_r(i, clusternum)) + return qfalse; + i = 0; + break; + } //end if + } //end for + } //end for + return qtrue; +} //end of the function AAS_FloodClusterAreasUsingReachabilities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_NumberClusterPortals(int clusternum) +{ + int i, portalnum; + aas_cluster_t *cluster; + aas_portal_t *portal; + + cluster = &aasworld.clusters[clusternum]; + for (i = 0; i < cluster->numportals; i++) + { + portalnum = aasworld.portalindex[cluster->firstportal + i]; + portal = &aasworld.portals[portalnum]; + if (portal->frontcluster == clusternum) + { + portal->clusterareanum[0] = cluster->numareas++; + } //end if + else + { + portal->clusterareanum[1] = cluster->numareas++; + } //end else + } //end for +} //end of the function AAS_NumberClusterPortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_NumberClusterAreas(int clusternum) +{ + int i, portalnum; + aas_cluster_t *cluster; + aas_portal_t *portal; + + aasworld.clusters[clusternum].numareas = 0; + aasworld.clusters[clusternum].numreachabilityareas = 0; + //number all areas in this cluster WITH reachabilities + for (i = 1; i < aasworld.numareas; i++) + { + // + if (aasworld.areasettings[i].cluster != clusternum) continue; + // + if (!AAS_AreaReachability(i)) continue; + // + aasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas; + //the cluster has an extra area + aasworld.clusters[clusternum].numareas++; + aasworld.clusters[clusternum].numreachabilityareas++; + } //end for + //number all portals in this cluster WITH reachabilities + cluster = &aasworld.clusters[clusternum]; + for (i = 0; i < cluster->numportals; i++) + { + portalnum = aasworld.portalindex[cluster->firstportal + i]; + portal = &aasworld.portals[portalnum]; + if (!AAS_AreaReachability(portal->areanum)) continue; + if (portal->frontcluster == clusternum) + { + portal->clusterareanum[0] = cluster->numareas++; + aasworld.clusters[clusternum].numreachabilityareas++; + } //end if + else + { + portal->clusterareanum[1] = cluster->numareas++; + aasworld.clusters[clusternum].numreachabilityareas++; + } //end else + } //end for + //number all areas in this cluster WITHOUT reachabilities + for (i = 1; i < aasworld.numareas; i++) + { + // + if (aasworld.areasettings[i].cluster != clusternum) continue; + // + if (AAS_AreaReachability(i)) continue; + // + aasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas; + //the cluster has an extra area + aasworld.clusters[clusternum].numareas++; + } //end for + //number all portals in this cluster WITHOUT reachabilities + cluster = &aasworld.clusters[clusternum]; + for (i = 0; i < cluster->numportals; i++) + { + portalnum = aasworld.portalindex[cluster->firstportal + i]; + portal = &aasworld.portals[portalnum]; + if (AAS_AreaReachability(portal->areanum)) continue; + if (portal->frontcluster == clusternum) + { + portal->clusterareanum[0] = cluster->numareas++; + } //end if + else + { + portal->clusterareanum[1] = cluster->numareas++; + } //end else + } //end for +} //end of the function AAS_NumberClusterAreas +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_FindClusters(void) +{ + int i; + aas_cluster_t *cluster; + + AAS_RemoveClusterAreas(); + // + for (i = 1; i < aasworld.numareas; i++) + { + //if the area is already part of a cluster + if (aasworld.areasettings[i].cluster) + continue; + // if not flooding through faces only use areas that have reachabilities + if (nofaceflood) + { + if (!aasworld.areasettings[i].numreachableareas) + continue; + } //end if + //if the area is a cluster portal + if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) + continue; + if (aasworld.numclusters >= AAS_MAX_CLUSTERS) + { + AAS_Error("AAS_MAX_CLUSTERS"); + return qfalse; + } //end if + cluster = &aasworld.clusters[aasworld.numclusters]; + cluster->numareas = 0; + cluster->numreachabilityareas = 0; + cluster->firstportal = aasworld.portalindexsize; + cluster->numportals = 0; + //flood the areas in this cluster + if (!AAS_FloodClusterAreas_r(i, aasworld.numclusters)) + return qfalse; + if (!AAS_FloodClusterAreasUsingReachabilities(aasworld.numclusters)) + return qfalse; + //number the cluster areas + //AAS_NumberClusterPortals(aasworld.numclusters); + AAS_NumberClusterAreas(aasworld.numclusters); + //Log_Write("cluster %d has %d areas\r\n", aasworld.numclusters, cluster->numareas); + aasworld.numclusters++; + } //end for + return qtrue; +} //end of the function AAS_FindClusters +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CreatePortals(void) +{ + int i; + aas_portal_t *portal; + + for (i = 1; i < aasworld.numareas; i++) + { + //if the area is a cluster portal + if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) + { + if (aasworld.numportals >= AAS_MAX_PORTALS) + { + AAS_Error("AAS_MAX_PORTALS"); + return; + } //end if + portal = &aasworld.portals[aasworld.numportals]; + portal->areanum = i; + portal->frontcluster = 0; + portal->backcluster = 0; + aasworld.numportals++; + } //end if + } //end for +} //end of the function AAS_CreatePortals +/* +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_MapContainsTeleporters(void) +{ + bsp_entity_t *entities, *ent; + char *classname; + + entities = AAS_ParseBSPEntities(); + + for (ent = entities; ent; ent = ent->next) + { + classname = AAS_ValueForBSPEpairKey(ent, "classname"); + if (classname && !strcmp(classname, "misc_teleporter")) + { + AAS_FreeBSPEntities(entities); + return qtrue; + } //end if + } //end for + return qfalse; +} //end of the function AAS_MapContainsTeleporters +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NonConvexFaces(aas_face_t *face1, aas_face_t *face2, int side1, int side2) +{ + int i, j, edgenum; + aas_plane_t *plane1, *plane2; + aas_edge_t *edge; + + + plane1 = &aasworld.planes[face1->planenum ^ side1]; + plane2 = &aasworld.planes[face2->planenum ^ side2]; + + //check if one of the points of face1 is at the back of the plane of face2 + for (i = 0; i < face1->numedges; i++) + { + edgenum = abs(aasworld.edgeindex[face1->firstedge + i]); + edge = &aasworld.edges[edgenum]; + for (j = 0; j < 2; j++) + { + if (DotProduct(plane2->normal, aasworld.vertexes[edge->v[j]]) - + plane2->dist < -0.01) return qtrue; + } //end for + } //end for + for (i = 0; i < face2->numedges; i++) + { + edgenum = abs(aasworld.edgeindex[face2->firstedge + i]); + edge = &aasworld.edges[edgenum]; + for (j = 0; j < 2; j++) + { + if (DotProduct(plane1->normal, aasworld.vertexes[edge->v[j]]) - + plane1->dist < -0.01) return qtrue; + } //end for + } //end for + + return qfalse; +} //end of the function AAS_NonConvexFaces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_CanMergeAreas(int *areanums, int numareas) +{ + int i, j, s, face1num, face2num, side1, side2, fn1, fn2; + aas_face_t *face1, *face2; + aas_area_t *area1, *area2; + + for (i = 0; i < numareas; i++) + { + area1 = &aasworld.areas[areanums[i]]; + for (fn1 = 0; fn1 < area1->numfaces; fn1++) + { + face1num = abs(aasworld.faceindex[area1->firstface + fn1]); + face1 = &aasworld.faces[face1num]; + side1 = face1->frontarea != areanums[i]; + //check if the face isn't a shared one with one of the other areas + for (s = 0; s < numareas; s++) + { + if (s == i) continue; + if (face1->frontarea == s || face1->backarea == s) break; + } //end for + //if the face was a shared one + if (s != numareas) continue; + // + for (j = 0; j < numareas; j++) + { + if (j == i) continue; + area2 = &aasworld.areas[areanums[j]]; + for (fn2 = 0; fn2 < area2->numfaces; fn2++) + { + face2num = abs(aasworld.faceindex[area2->firstface + fn2]); + face2 = &aasworld.faces[face2num]; + side2 = face2->frontarea != areanums[j]; + //check if the face isn't a shared one with one of the other areas + for (s = 0; s < numareas; s++) + { + if (s == j) continue; + if (face2->frontarea == s || face2->backarea == s) break; + } //end for + //if the face was a shared one + if (s != numareas) continue; + // + if (AAS_NonConvexFaces(face1, face2, side1, side2)) return qfalse; + } //end for + } //end for + } //end for + } //end for + return qtrue; +} //end of the function AAS_CanMergeAreas +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_NonConvexEdges(aas_edge_t *edge1, aas_edge_t *edge2, int side1, int side2, int planenum) +{ + int i; + vec3_t edgevec1, edgevec2, normal1, normal2; + float dist1, dist2; + aas_plane_t *plane; + + plane = &aasworld.planes[planenum]; + VectorSubtract(aasworld.vertexes[edge1->v[1]], aasworld.vertexes[edge1->v[0]], edgevec1); + VectorSubtract(aasworld.vertexes[edge2->v[1]], aasworld.vertexes[edge2->v[0]], edgevec2); + if (side1) VectorInverse(edgevec1); + if (side2) VectorInverse(edgevec2); + // + CrossProduct(edgevec1, plane->normal, normal1); + dist1 = DotProduct(normal1, aasworld.vertexes[edge1->v[0]]); + CrossProduct(edgevec2, plane->normal, normal2); + dist2 = DotProduct(normal2, aasworld.vertexes[edge2->v[0]]); + + for (i = 0; i < 2; i++) + { + if (DotProduct(aasworld.vertexes[edge1->v[i]], normal2) - dist2 < -0.01) return qfalse; + } //end for + for (i = 0; i < 2; i++) + { + if (DotProduct(aasworld.vertexes[edge2->v[i]], normal1) - dist1 < -0.01) return qfalse; + } //end for + return qtrue; +} //end of the function AAS_NonConvexEdges +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_CanMergeFaces(int *facenums, int numfaces, int planenum) +{ + int i, j, s, edgenum1, edgenum2, side1, side2, en1, en2, ens; + aas_face_t *face1, *face2, *otherface; + aas_edge_t *edge1, *edge2; + + for (i = 0; i < numfaces; i++) + { + face1 = &aasworld.faces[facenums[i]]; + for (en1 = 0; en1 < face1->numedges; en1++) + { + edgenum1 = aasworld.edgeindex[face1->firstedge + en1]; + side1 = (edgenum1 < 0) ^ (face1->planenum != planenum); + edgenum1 = abs(edgenum1); + edge1 = &aasworld.edges[edgenum1]; + //check if the edge is shared with another face + for (s = 0; s < numfaces; s++) + { + if (s == i) continue; + otherface = &aasworld.faces[facenums[s]]; + for (ens = 0; ens < otherface->numedges; ens++) + { + if (edgenum1 == abs(aasworld.edgeindex[otherface->firstedge + ens])) break; + } //end for + if (ens != otherface->numedges) break; + } //end for + //if the edge was shared + if (s != numfaces) continue; + // + for (j = 0; j < numfaces; j++) + { + if (j == i) continue; + face2 = &aasworld.faces[facenums[j]]; + for (en2 = 0; en2 < face2->numedges; en2++) + { + edgenum2 = aasworld.edgeindex[face2->firstedge + en2]; + side2 = (edgenum2 < 0) ^ (face2->planenum != planenum); + edgenum2 = abs(edgenum2); + edge2 = &aasworld.edges[edgenum2]; + //check if the edge is shared with another face + for (s = 0; s < numfaces; s++) + { + if (s == i) continue; + otherface = &aasworld.faces[facenums[s]]; + for (ens = 0; ens < otherface->numedges; ens++) + { + if (edgenum2 == abs(aasworld.edgeindex[otherface->firstedge + ens])) break; + } //end for + if (ens != otherface->numedges) break; + } //end for + //if the edge was shared + if (s != numfaces) continue; + // + if (AAS_NonConvexEdges(edge1, edge2, side1, side2, planenum)) return qfalse; + } //end for + } //end for + } //end for + } //end for + return qtrue; +} //end of the function AAS_CanMergeFaces*/ +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ConnectedAreas_r(int *areanums, int numareas, int *connectedareas, int curarea) +{ + int i, j, otherareanum, facenum; + aas_area_t *area; + aas_face_t *face; + + connectedareas[curarea] = qtrue; + area = &aasworld.areas[areanums[curarea]]; + for (i = 0; i < area->numfaces; i++) + { + facenum = abs(aasworld.faceindex[area->firstface + i]); + face = &aasworld.faces[facenum]; + //if the face is solid + if (face->faceflags & FACE_SOLID) continue; + //get the area at the other side of the face + if (face->frontarea != areanums[curarea]) otherareanum = face->frontarea; + else otherareanum = face->backarea; + //check if the face is leading to one of the other areas + for (j = 0; j < numareas; j++) + { + if (areanums[j] == otherareanum) break; + } //end for + //if the face isn't leading to one of the other areas + if (j == numareas) continue; + //if the other area is already connected + if (connectedareas[j]) continue; + //recursively proceed with the other area + AAS_ConnectedAreas_r(areanums, numareas, connectedareas, j); + } //end for +} //end of the function AAS_ConnectedAreas_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_ConnectedAreas(int *areanums, int numareas) +{ + int connectedareas[MAX_PORTALAREAS], i; + + Com_Memset(connectedareas, 0, sizeof(connectedareas)); + if (numareas < 1) return qfalse; + if (numareas == 1) return qtrue; + AAS_ConnectedAreas_r(areanums, numareas, connectedareas, 0); + for (i = 0; i < numareas; i++) + { + if (!connectedareas[i]) return qfalse; + } //end for + return qtrue; +} //end of the function AAS_ConnectedAreas +//=========================================================================== +// gets adjacent areas with less presence types recursively +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_GetAdjacentAreasWithLessPresenceTypes_r(int *areanums, int numareas, int curareanum) +{ + int i, j, presencetype, otherpresencetype, otherareanum, facenum; + aas_area_t *area; + aas_face_t *face; + + areanums[numareas++] = curareanum; + area = &aasworld.areas[curareanum]; + presencetype = aasworld.areasettings[curareanum].presencetype; + for (i = 0; i < area->numfaces; i++) + { + facenum = abs(aasworld.faceindex[area->firstface + i]); + face = &aasworld.faces[facenum]; + //if the face is solid + if (face->faceflags & FACE_SOLID) continue; + //the area at the other side of the face + if (face->frontarea != curareanum) otherareanum = face->frontarea; + else otherareanum = face->backarea; + // + otherpresencetype = aasworld.areasettings[otherareanum].presencetype; + //if the other area has less presence types + if ((presencetype & ~otherpresencetype) && + !(otherpresencetype & ~presencetype)) + { + //check if the other area isn't already in the list + for (j = 0; j < numareas; j++) + { + if (otherareanum == areanums[j]) break; + } //end for + //if the other area isn't already in the list + if (j == numareas) + { + if (numareas >= MAX_PORTALAREAS) + { + AAS_Error("MAX_PORTALAREAS"); + return numareas; + } //end if + numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r(areanums, numareas, otherareanum); + } //end if + } //end if + } //end for + return numareas; +} //end of the function AAS_GetAdjacentAreasWithLessPresenceTypes_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_CheckAreaForPossiblePortals(int areanum) +{ + int i, j, k, fen, ben, frontedgenum, backedgenum, facenum; + int areanums[MAX_PORTALAREAS], numareas, otherareanum; + int numareafrontfaces[MAX_PORTALAREAS], numareabackfaces[MAX_PORTALAREAS]; + int frontfacenums[MAX_PORTALAREAS], backfacenums[MAX_PORTALAREAS]; + int numfrontfaces, numbackfaces; + int frontareanums[MAX_PORTALAREAS], backareanums[MAX_PORTALAREAS]; + int numfrontareas, numbackareas; + int frontplanenum, backplanenum, faceplanenum; + aas_area_t *area; + aas_face_t *frontface, *backface, *face; + + //if it isn't already a portal + if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return 0; + //it must be a grounded area + if (!(aasworld.areasettings[areanum].areaflags & AREA_GROUNDED)) return 0; + // + Com_Memset(numareafrontfaces, 0, sizeof(numareafrontfaces)); + Com_Memset(numareabackfaces, 0, sizeof(numareabackfaces)); + numareas = numfrontfaces = numbackfaces = 0; + numfrontareas = numbackareas = 0; + frontplanenum = backplanenum = -1; + //add any adjacent areas with less presence types + numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r(areanums, 0, areanum); + // + for (i = 0; i < numareas; i++) + { + area = &aasworld.areas[areanums[i]]; + for (j = 0; j < area->numfaces; j++) + { + facenum = abs(aasworld.faceindex[area->firstface + j]); + face = &aasworld.faces[facenum]; + //if the face is solid + if (face->faceflags & FACE_SOLID) continue; + //check if the face is shared with one of the other areas + for (k = 0; k < numareas; k++) + { + if (k == i) continue; + if (face->frontarea == areanums[k] || face->backarea == areanums[k]) break; + } //end for + //if the face is shared + if (k != numareas) continue; + //the number of the area at the other side of the face + if (face->frontarea == areanums[i]) otherareanum = face->backarea; + else otherareanum = face->frontarea; + //if the other area already is a cluter portal + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) return 0; + //number of the plane of the area + faceplanenum = face->planenum & ~1; + // + if (frontplanenum < 0 || faceplanenum == frontplanenum) + { + frontplanenum = faceplanenum; + frontfacenums[numfrontfaces++] = facenum; + for (k = 0; k < numfrontareas; k++) + { + if (frontareanums[k] == otherareanum) break; + } //end for + if (k == numfrontareas) frontareanums[numfrontareas++] = otherareanum; + numareafrontfaces[i]++; + } //end if + else if (backplanenum < 0 || faceplanenum == backplanenum) + { + backplanenum = faceplanenum; + backfacenums[numbackfaces++] = facenum; + for (k = 0; k < numbackareas; k++) + { + if (backareanums[k] == otherareanum) break; + } //end for + if (k == numbackareas) backareanums[numbackareas++] = otherareanum; + numareabackfaces[i]++; + } //end else + else + { + return 0; + } //end else + } //end for + } //end for + //every area should have at least one front face and one back face + for (i = 0; i < numareas; i++) + { + if (!numareafrontfaces[i] || !numareabackfaces[i]) return 0; + } //end for + //the front areas should all be connected + if (!AAS_ConnectedAreas(frontareanums, numfrontareas)) return 0; + //the back areas should all be connected + if (!AAS_ConnectedAreas(backareanums, numbackareas)) return 0; + //none of the front faces should have a shared edge with a back face + for (i = 0; i < numfrontfaces; i++) + { + frontface = &aasworld.faces[frontfacenums[i]]; + for (fen = 0; fen < frontface->numedges; fen++) + { + frontedgenum = abs(aasworld.edgeindex[frontface->firstedge + fen]); + for (j = 0; j < numbackfaces; j++) + { + backface = &aasworld.faces[backfacenums[j]]; + for (ben = 0; ben < backface->numedges; ben++) + { + backedgenum = abs(aasworld.edgeindex[backface->firstedge + ben]); + if (frontedgenum == backedgenum) break; + } //end for + if (ben != backface->numedges) break; + } //end for + if (j != numbackfaces) break; + } //end for + if (fen != frontface->numedges) break; + } //end for + if (i != numfrontfaces) return 0; + //set the cluster portal contents + for (i = 0; i < numareas; i++) + { + aasworld.areasettings[areanums[i]].contents |= AREACONTENTS_CLUSTERPORTAL; + //this area can be used as a route portal + aasworld.areasettings[areanums[i]].contents |= AREACONTENTS_ROUTEPORTAL; + Log_Write("possible portal: %d\r\n", areanums[i]); + } //end for + // + return numareas; +} //end of the function AAS_CheckAreaForPossiblePortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FindPossiblePortals(void) +{ + int i, numpossibleportals; + + numpossibleportals = 0; + for (i = 1; i < aasworld.numareas; i++) + { + numpossibleportals += AAS_CheckAreaForPossiblePortals(i); + } //end for + botimport.Print(PRT_MESSAGE, "\r%6d possible portal areas\n", numpossibleportals); +} //end of the function AAS_FindPossiblePortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveAllPortals(void) +{ + int i; + + for (i = 1; i < aasworld.numareas; i++) + { + aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; + } //end for +} //end of the function AAS_RemoveAllPortals + +#if 0 +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FloodCluster_r(int areanum, int clusternum) +{ + int i, otherareanum; + aas_face_t *face; + aas_area_t *area; + + //set cluster mark + aasworld.areasettings[areanum].cluster = clusternum; + //if the area is a portal + //if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return; + // + area = &aasworld.areas[areanum]; + //use area faces to flood into adjacent areas + for (i = 0; i < area->numfaces; i++) + { + face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])]; + // + if (face->frontarea != areanum) otherareanum = face->frontarea; + else otherareanum = face->backarea; + //if there's no area at the other side + if (!otherareanum) continue; + //if the area is a portal + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; + //if the area is already marked + if (aasworld.areasettings[otherareanum].cluster) continue; + // + AAS_FloodCluster_r(otherareanum, clusternum); + } //end for + //use the reachabilities to flood into other areas + for (i = 0; i < aasworld.areasettings[areanum].numreachableareas; i++) + { + otherareanum = aasworld.reachability[ + aasworld.areasettings[areanum].firstreachablearea + i].areanum; + if (!otherareanum) + { + continue; + AAS_Error("reachability %d has zero area\n", aasworld.areasettings[areanum].firstreachablearea + i); + } //end if + //if the area is a portal + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; + //if the area is already marked + if (aasworld.areasettings[otherareanum].cluster) continue; + // + AAS_FloodCluster_r(otherareanum, clusternum); + } //end for +} //end of the function AAS_FloodCluster_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveTeleporterPortals(void) +{ + int i, j, areanum; + + for (i = 1; i < aasworld.numareas; i++) + { + for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) + { + areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; + if (aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].traveltype == TRAVEL_TELEPORT) + { + aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; + aasworld.areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; + break; + } //end if + } //end for + } //end for +} //end of the function AAS_RemoveTeleporterPortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FloodClusterReachabilities(int clusternum) +{ + int i, j, areanum; + + for (i = 1; i < aasworld.numareas; i++) + { + //if this area already has a cluster set + if (aasworld.areasettings[i].cluster) continue; + //if this area is a cluster portal + if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) continue; + //loop over the reachable areas from this area + for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) + { + //the reachable area + areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; + //if this area is a cluster portal + if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; + //if this area has a cluster set + if (aasworld.areasettings[areanum].cluster == clusternum) + { + AAS_FloodCluster_r(i, clusternum); + i = 0; + break; + } //end if + } //end for + } //end for +} //end of the function AAS_FloodClusterReachabilities + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveNotClusterClosingPortals(void) +{ + int i, j, k, facenum, otherareanum, nonclosingportals; + aas_area_t *area; + aas_face_t *face; + + AAS_RemoveTeleporterPortals(); + // + nonclosingportals = 0; + for (i = 1; i < aasworld.numareas; i++) + { + if (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue; + //find a non-portal area adjacent to the portal area and flood + //the cluster from there + area = &aasworld.areas[i]; + for (j = 0; j < area->numfaces; j++) + { + facenum = abs(aasworld.faceindex[area->firstface + j]); + face = &aasworld.faces[facenum]; + // + if (face->frontarea != i) otherareanum = face->frontarea; + else otherareanum = face->backarea; + // + if (!otherareanum) continue; + // + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) + { + continue; + } //end if + //reset all cluster fields + AAS_RemoveClusterAreas(); + // + AAS_FloodCluster_r(otherareanum, 1); + AAS_FloodClusterReachabilities(1); + //check if all adjacent non-portal areas have a cluster set + for (k = 0; k < area->numfaces; k++) + { + facenum = abs(aasworld.faceindex[area->firstface + k]); + face = &aasworld.faces[facenum]; + // + if (face->frontarea != i) otherareanum = face->frontarea; + else otherareanum = face->backarea; + // + if (!otherareanum) continue; + // + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) + { + continue; + } //end if + // + if (!aasworld.areasettings[otherareanum].cluster) break; + } //end for + //if all adjacent non-portal areas have a cluster set then the portal + //didn't seal a cluster + if (k >= area->numfaces) + { + aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; + nonclosingportals++; + //recheck all the other portals again + i = 0; + break; + } //end if + } //end for + } //end for + botimport.Print(PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals); +} //end of the function AAS_RemoveNotClusterClosingPortals + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +void AAS_RemoveNotClusterClosingPortals(void) +{ + int i, j, facenum, otherareanum, nonclosingportals, numseperatedclusters; + aas_area_t *area; + aas_face_t *face; + + AAS_RemoveTeleporterPortals(); + // + nonclosingportals = 0; + for (i = 1; i < aasworld.numareas; i++) + { + if (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue; + // + numseperatedclusters = 0; + //reset all cluster fields + AAS_RemoveClusterAreas(); + //find a non-portal area adjacent to the portal area and flood + //the cluster from there + area = &aasworld.areas[i]; + for (j = 0; j < area->numfaces; j++) + { + facenum = abs(aasworld.faceindex[area->firstface + j]); + face = &aasworld.faces[facenum]; + // + if (face->frontarea != i) otherareanum = face->frontarea; + else otherareanum = face->backarea; + //if not solid at the other side of the face + if (!otherareanum) continue; + //don't flood into other portals + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; + //if the area already has a cluster set + if (aasworld.areasettings[otherareanum].cluster) continue; + //another cluster is seperated by this portal + numseperatedclusters++; + //flood the cluster + AAS_FloodCluster_r(otherareanum, numseperatedclusters); + AAS_FloodClusterReachabilities(numseperatedclusters); + } //end for + //use the reachabilities to flood into other areas + for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) + { + otherareanum = aasworld.reachability[ + aasworld.areasettings[i].firstreachablearea + j].areanum; + //this should never be qtrue but we check anyway + if (!otherareanum) continue; + //don't flood into other portals + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; + //if the area already has a cluster set + if (aasworld.areasettings[otherareanum].cluster) continue; + //another cluster is seperated by this portal + numseperatedclusters++; + //flood the cluster + AAS_FloodCluster_r(otherareanum, numseperatedclusters); + AAS_FloodClusterReachabilities(numseperatedclusters); + } //end for + //a portal must seperate no more and no less than 2 clusters + if (numseperatedclusters != 2) + { + aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; + nonclosingportals++; + //recheck all the other portals again + i = 0; + } //end if + } //end for + botimport.Print(PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals); +} //end of the function AAS_RemoveNotClusterClosingPortals + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +void AAS_AddTeleporterPortals(void) +{ + int j, area2num, facenum, otherareanum; + char *target, *targetname, *classname; + bsp_entity_t *entities, *ent, *dest; + vec3_t origin, destorigin, mins, maxs, end; + vec3_t bbmins, bbmaxs; + aas_area_t *area; + aas_face_t *face; + aas_trace_t trace; + aas_link_t *areas, *link; + + entities = AAS_ParseBSPEntities(); + + for (ent = entities; ent; ent = ent->next) + { + classname = AAS_ValueForBSPEpairKey(ent, "classname"); + if (classname && !strcmp(classname, "misc_teleporter")) + { + if (!AAS_VectorForBSPEpairKey(ent, "origin", origin)) + { + botimport.Print(PRT_ERROR, "teleporter (%s) without origin\n", target); + continue; + } //end if + // + target = AAS_ValueForBSPEpairKey(ent, "target"); + if (!target) + { + botimport.Print(PRT_ERROR, "teleporter (%s) without target\n", target); + continue; + } //end if + for (dest = entities; dest; dest = dest->next) + { + classname = AAS_ValueForBSPEpairKey(dest, "classname"); + if (classname && !strcmp(classname, "misc_teleporter_dest")) + { + targetname = AAS_ValueForBSPEpairKey(dest, "targetname"); + if (targetname && !strcmp(targetname, target)) + { + break; + } //end if + } //end if + } //end for + if (!dest) + { + botimport.Print(PRT_ERROR, "teleporter without destination (%s)\n", target); + continue; + } //end if + if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin)) + { + botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target); + continue; + } //end if + destorigin[2] += 24; //just for q2e1m2, the dork has put the telepads in the ground + VectorCopy(destorigin, end); + end[2] -= 100; + trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1); + if (trace.startsolid) + { + botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target); + continue; + } //end if + VectorCopy(trace.endpos, destorigin); + area2num = AAS_PointAreaNum(destorigin); + //reset all cluster fields + for (j = 0; j < aasworld.numareas; j++) + { + aasworld.areasettings[j].cluster = 0; + } //end for + // + VectorSet(mins, -8, -8, 8); + VectorSet(maxs, 8, 8, 24); + // + AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs); + // + VectorAdd(origin, mins, mins); + VectorAdd(origin, maxs, maxs); + //add bounding box size + VectorSubtract(mins, bbmaxs, mins); + VectorSubtract(maxs, bbmins, maxs); + //link an invalid (-1) entity + areas = AAS_AASLinkEntity(mins, maxs, -1); + // + for (link = areas; link; link = link->next_area) + { + if (!AAS_AreaGrounded(link->areanum)) continue; + //add the teleporter portal mark + aasworld.areasettings[link->areanum].contents |= AREACONTENTS_CLUSTERPORTAL | + AREACONTENTS_TELEPORTAL; + } //end for + // + for (link = areas; link; link = link->next_area) + { + if (!AAS_AreaGrounded(link->areanum)) continue; + //find a non-portal area adjacent to the portal area and flood + //the cluster from there + area = &aasworld.areas[link->areanum]; + for (j = 0; j < area->numfaces; j++) + { + facenum = abs(aasworld.faceindex[area->firstface + j]); + face = &aasworld.faces[facenum]; + // + if (face->frontarea != link->areanum) otherareanum = face->frontarea; + else otherareanum = face->backarea; + // + if (!otherareanum) continue; + // + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) + { + continue; + } //end if + // + AAS_FloodCluster_r(otherareanum, 1); + } //end for + } //end for + //if the teleport destination IS in the same cluster + if (aasworld.areasettings[area2num].cluster) + { + for (link = areas; link; link = link->next_area) + { + if (!AAS_AreaGrounded(link->areanum)) continue; + //add the teleporter portal mark + aasworld.areasettings[link->areanum].contents &= ~(AREACONTENTS_CLUSTERPORTAL | + AREACONTENTS_TELEPORTAL); + } //end for + } //end if + } //end if + } //end for + AAS_FreeBSPEntities(entities); +} //end of the function AAS_AddTeleporterPortals + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_AddTeleporterPortals(void) +{ + int i, j, areanum; + + for (i = 1; i < aasworld.numareas; i++) + { + for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) + { + if (aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].traveltype != TRAVEL_TELEPORT) continue; + areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; + aasworld.areasettings[areanum].contents |= AREACONTENTS_CLUSTERPORTAL; + } //end for + } //end for +} //end of the function AAS_AddTeleporterPortals + +#endif + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_TestPortals(void) +{ + int i; + aas_portal_t *portal; + + for (i = 1; i < aasworld.numportals; i++) + { + portal = &aasworld.portals[i]; + if (!portal->frontcluster) + { + aasworld.areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; + Log_Write("portal area %d has no front cluster\r\n", portal->areanum); + return qfalse; + } //end if + if (!portal->backcluster) + { + aasworld.areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; + Log_Write("portal area %d has no back cluster\r\n", portal->areanum); + return qfalse; + } //end if + } //end for + return qtrue; +} //end of the function +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CountForcedClusterPortals(void) +{ + int num, i; + + num = 0; + for (i = 1; i < aasworld.numareas; i++) + { + if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) + { + Log_Write("area %d is a forced portal area\r\n", i); + num++; + } //end if + } //end for + botimport.Print(PRT_MESSAGE, "%6d forced portal areas\n", num); +} //end of the function AAS_CountForcedClusterPortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CreateViewPortals(void) +{ + int i; + + for (i = 1; i < aasworld.numareas; i++) + { + if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) + { + aasworld.areasettings[i].contents |= AREACONTENTS_VIEWPORTAL; + } //end if + } //end for +} //end of the function AAS_CreateViewPortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SetViewPortalsAsClusterPortals(void) +{ + int i; + + for (i = 1; i < aasworld.numareas; i++) + { + if (aasworld.areasettings[i].contents & AREACONTENTS_VIEWPORTAL) + { + aasworld.areasettings[i].contents |= AREACONTENTS_CLUSTERPORTAL; + } //end if + } //end for +} //end of the function AAS_SetViewPortalsAsClusterPortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitClustering(void) +{ + int i, removedPortalAreas; + int n, total, numreachabilityareas; + + if (!aasworld.loaded) return; + //if there are clusters + if (aasworld.numclusters >= 1) + { +#ifndef BSPC + //if clustering isn't forced + if (!((int)LibVarGetValue("forceclustering")) && + !((int)LibVarGetValue("forcereachability"))) return; +#endif + } //end if + //set all view portals as cluster portals in case we re-calculate the reachabilities and clusters (with -reach) + AAS_SetViewPortalsAsClusterPortals(); + //count the number of forced cluster portals + AAS_CountForcedClusterPortals(); + //remove all area cluster marks + AAS_RemoveClusterAreas(); + //find possible cluster portals + AAS_FindPossiblePortals(); + //craete portals to for the bot view + AAS_CreateViewPortals(); + //remove all portals that are not closing a cluster + //AAS_RemoveNotClusterClosingPortals(); + //initialize portal memory + if (aasworld.portals) FreeMemory(aasworld.portals); + aasworld.portals = (aas_portal_t *) GetClearedMemory(AAS_MAX_PORTALS * sizeof(aas_portal_t)); + //initialize portal index memory + if (aasworld.portalindex) FreeMemory(aasworld.portalindex); + aasworld.portalindex = (aas_portalindex_t *) GetClearedMemory(AAS_MAX_PORTALINDEXSIZE * sizeof(aas_portalindex_t)); + //initialize cluster memory + if (aasworld.clusters) FreeMemory(aasworld.clusters); + aasworld.clusters = (aas_cluster_t *) GetClearedMemory(AAS_MAX_CLUSTERS * sizeof(aas_cluster_t)); + // + removedPortalAreas = 0; + botimport.Print(PRT_MESSAGE, "\r%6d removed portal areas", removedPortalAreas); + while(1) + { + botimport.Print(PRT_MESSAGE, "\r%6d", removedPortalAreas); + //initialize the number of portals and clusters + aasworld.numportals = 1; //portal 0 is a dummy + aasworld.portalindexsize = 0; + aasworld.numclusters = 1; //cluster 0 is a dummy + //create the portals from the portal areas + AAS_CreatePortals(); + // + removedPortalAreas++; + //find the clusters + if (!AAS_FindClusters()) + continue; + //test the portals + if (!AAS_TestPortals()) + continue; + // + break; + } //end while + botimport.Print(PRT_MESSAGE, "\n"); + //the AAS file should be saved + aasworld.savefile = qtrue; + //write the portal areas to the log file + for (i = 1; i < aasworld.numportals; i++) + { + Log_Write("portal %d: area %d\r\n", i, aasworld.portals[i].areanum); + } //end for + // report cluster info + botimport.Print(PRT_MESSAGE, "%6d portals created\n", aasworld.numportals); + botimport.Print(PRT_MESSAGE, "%6d clusters created\n", aasworld.numclusters); + for (i = 1; i < aasworld.numclusters; i++) + { + botimport.Print(PRT_MESSAGE, "cluster %d has %d reachability areas\n", i, + aasworld.clusters[i].numreachabilityareas); + } //end for + // report AAS file efficiency + numreachabilityareas = 0; + total = 0; + for (i = 0; i < aasworld.numclusters; i++) { + n = aasworld.clusters[i].numreachabilityareas; + numreachabilityareas += n; + total += n * n; + } + total += numreachabilityareas * aasworld.numportals; + // + botimport.Print(PRT_MESSAGE, "%6i total reachability areas\n", numreachabilityareas); + botimport.Print(PRT_MESSAGE, "%6i AAS memory/CPU usage (the lower the better)\n", total * 3); +} //end of the function AAS_InitClustering diff --git a/reaction/engine/code/botlib/be_aas_cluster.h b/reaction/engine/code/botlib/be_aas_cluster.h new file mode 100644 index 00000000..e36697d1 --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_cluster.h @@ -0,0 +1,38 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_cluster.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_cluster.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +//initialize the AAS clustering +void AAS_InitClustering(void); +// +void AAS_SetViewPortalsAsClusterPortals(void); +#endif //AASINTERN + diff --git a/reaction/engine/code/botlib/be_aas_debug.c b/reaction/engine/code/botlib/be_aas_debug.c new file mode 100644 index 00000000..ab44bc0b --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_debug.c @@ -0,0 +1,777 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_debug.c + * + * desc: AAS debug code + * + * $Archive: /MissionPack/code/botlib/be_aas_debug.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_libvar.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_interface.h" +#include "be_aas_funcs.h" +#include "be_aas_def.h" + +#define MAX_DEBUGLINES 1024 +#define MAX_DEBUGPOLYGONS 8192 + +int debuglines[MAX_DEBUGLINES]; +int debuglinevisible[MAX_DEBUGLINES]; +int numdebuglines; + +static int debugpolygons[MAX_DEBUGPOLYGONS]; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ClearShownPolygons(void) +{ + int i; +//* + for (i = 0; i < MAX_DEBUGPOLYGONS; i++) + { + if (debugpolygons[i]) botimport.DebugPolygonDelete(debugpolygons[i]); + debugpolygons[i] = 0; + } //end for +//*/ +/* + for (i = 0; i < MAX_DEBUGPOLYGONS; i++) + { + botimport.DebugPolygonDelete(i); + debugpolygons[i] = 0; + } //end for +*/ +} //end of the function AAS_ClearShownPolygons +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowPolygon(int color, int numpoints, vec3_t *points) +{ + int i; + + for (i = 0; i < MAX_DEBUGPOLYGONS; i++) + { + if (!debugpolygons[i]) + { + debugpolygons[i] = botimport.DebugPolygonCreate(color, numpoints, points); + break; + } //end if + } //end for +} //end of the function AAS_ShowPolygon +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ClearShownDebugLines(void) +{ + int i; + + //make all lines invisible + for (i = 0; i < MAX_DEBUGLINES; i++) + { + if (debuglines[i]) + { + //botimport.DebugLineShow(debuglines[i], NULL, NULL, LINECOLOR_NONE); + botimport.DebugLineDelete(debuglines[i]); + debuglines[i] = 0; + debuglinevisible[i] = qfalse; + } //end if + } //end for +} //end of the function AAS_ClearShownDebugLines +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DebugLine(vec3_t start, vec3_t end, int color) +{ + int line; + + for (line = 0; line < MAX_DEBUGLINES; line++) + { + if (!debuglines[line]) + { + debuglines[line] = botimport.DebugLineCreate(); + debuglinevisible[line] = qfalse; + numdebuglines++; + } //end if + if (!debuglinevisible[line]) + { + botimport.DebugLineShow(debuglines[line], start, end, color); + debuglinevisible[line] = qtrue; + return; + } //end else + } //end for +} //end of the function AAS_DebugLine +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_PermanentLine(vec3_t start, vec3_t end, int color) +{ + int line; + + line = botimport.DebugLineCreate(); + botimport.DebugLineShow(line, start, end, color); +} //end of the function AAS_PermenentLine +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DrawPermanentCross(vec3_t origin, float size, int color) +{ + int i, debugline; + vec3_t start, end; + + for (i = 0; i < 3; i++) + { + VectorCopy(origin, start); + start[i] += size; + VectorCopy(origin, end); + end[i] -= size; + AAS_DebugLine(start, end, color); + debugline = botimport.DebugLineCreate(); + botimport.DebugLineShow(debugline, start, end, color); + } //end for +} //end of the function AAS_DrawPermanentCross +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DrawPlaneCross(vec3_t point, vec3_t normal, float dist, int type, int color) +{ + int n0, n1, n2, j, line, lines[2]; + vec3_t start1, end1, start2, end2; + + //make a cross in the hit plane at the hit point + VectorCopy(point, start1); + VectorCopy(point, end1); + VectorCopy(point, start2); + VectorCopy(point, end2); + + n0 = type % 3; + n1 = (type + 1) % 3; + n2 = (type + 2) % 3; + start1[n1] -= 6; + start1[n2] -= 6; + end1[n1] += 6; + end1[n2] += 6; + start2[n1] += 6; + start2[n2] -= 6; + end2[n1] -= 6; + end2[n2] += 6; + + start1[n0] = (dist - (start1[n1] * normal[n1] + + start1[n2] * normal[n2])) / normal[n0]; + end1[n0] = (dist - (end1[n1] * normal[n1] + + end1[n2] * normal[n2])) / normal[n0]; + start2[n0] = (dist - (start2[n1] * normal[n1] + + start2[n2] * normal[n2])) / normal[n0]; + end2[n0] = (dist - (end2[n1] * normal[n1] + + end2[n2] * normal[n2])) / normal[n0]; + + for (j = 0, line = 0; j < 2 && line < MAX_DEBUGLINES; line++) + { + if (!debuglines[line]) + { + debuglines[line] = botimport.DebugLineCreate(); + lines[j++] = debuglines[line]; + debuglinevisible[line] = qtrue; + numdebuglines++; + } //end if + else if (!debuglinevisible[line]) + { + lines[j++] = debuglines[line]; + debuglinevisible[line] = qtrue; + } //end else + } //end for + botimport.DebugLineShow(lines[0], start1, end1, color); + botimport.DebugLineShow(lines[1], start2, end2, color); +} //end of the function AAS_DrawPlaneCross +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowBoundingBox(vec3_t origin, vec3_t mins, vec3_t maxs) +{ + vec3_t bboxcorners[8]; + int lines[3]; + int i, j, line; + + //upper corners + bboxcorners[0][0] = origin[0] + maxs[0]; + bboxcorners[0][1] = origin[1] + maxs[1]; + bboxcorners[0][2] = origin[2] + maxs[2]; + // + bboxcorners[1][0] = origin[0] + mins[0]; + bboxcorners[1][1] = origin[1] + maxs[1]; + bboxcorners[1][2] = origin[2] + maxs[2]; + // + bboxcorners[2][0] = origin[0] + mins[0]; + bboxcorners[2][1] = origin[1] + mins[1]; + bboxcorners[2][2] = origin[2] + maxs[2]; + // + bboxcorners[3][0] = origin[0] + maxs[0]; + bboxcorners[3][1] = origin[1] + mins[1]; + bboxcorners[3][2] = origin[2] + maxs[2]; + //lower corners + Com_Memcpy(bboxcorners[4], bboxcorners[0], sizeof(vec3_t) * 4); + for (i = 0; i < 4; i++) bboxcorners[4 + i][2] = origin[2] + mins[2]; + //draw bounding box + for (i = 0; i < 4; i++) + { + for (j = 0, line = 0; j < 3 && line < MAX_DEBUGLINES; line++) + { + if (!debuglines[line]) + { + debuglines[line] = botimport.DebugLineCreate(); + lines[j++] = debuglines[line]; + debuglinevisible[line] = qtrue; + numdebuglines++; + } //end if + else if (!debuglinevisible[line]) + { + lines[j++] = debuglines[line]; + debuglinevisible[line] = qtrue; + } //end else + } //end for + //top plane + botimport.DebugLineShow(lines[0], bboxcorners[i], + bboxcorners[(i+1)&3], LINECOLOR_RED); + //bottom plane + botimport.DebugLineShow(lines[1], bboxcorners[4+i], + bboxcorners[4+((i+1)&3)], LINECOLOR_RED); + //vertical lines + botimport.DebugLineShow(lines[2], bboxcorners[i], + bboxcorners[4+i], LINECOLOR_RED); + } //end for +} //end of the function AAS_ShowBoundingBox +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowFace(int facenum) +{ + int i, color, edgenum; + aas_edge_t *edge; + aas_face_t *face; + aas_plane_t *plane; + vec3_t start, end; + + color = LINECOLOR_YELLOW; + //check if face number is in range + if (facenum >= aasworld.numfaces) + { + botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); + } //end if + face = &aasworld.faces[facenum]; + //walk through the edges of the face + for (i = 0; i < face->numedges; i++) + { + //edge number + edgenum = abs(aasworld.edgeindex[face->firstedge + i]); + //check if edge number is in range + if (edgenum >= aasworld.numedges) + { + botimport.Print(PRT_ERROR, "edgenum %d out of range\n", edgenum); + } //end if + edge = &aasworld.edges[edgenum]; + if (color == LINECOLOR_RED) color = LINECOLOR_GREEN; + else if (color == LINECOLOR_GREEN) color = LINECOLOR_BLUE; + else if (color == LINECOLOR_BLUE) color = LINECOLOR_YELLOW; + else color = LINECOLOR_RED; + AAS_DebugLine(aasworld.vertexes[edge->v[0]], + aasworld.vertexes[edge->v[1]], + color); + } //end for + plane = &aasworld.planes[face->planenum]; + edgenum = abs(aasworld.edgeindex[face->firstedge]); + edge = &aasworld.edges[edgenum]; + VectorCopy(aasworld.vertexes[edge->v[0]], start); + VectorMA(start, 20, plane->normal, end); + AAS_DebugLine(start, end, LINECOLOR_RED); +} //end of the function AAS_ShowFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowFacePolygon(int facenum, int color, int flip) +{ + int i, edgenum, numpoints; + vec3_t points[128]; + aas_edge_t *edge; + aas_face_t *face; + + //check if face number is in range + if (facenum >= aasworld.numfaces) + { + botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); + } //end if + face = &aasworld.faces[facenum]; + //walk through the edges of the face + numpoints = 0; + if (flip) + { + for (i = face->numedges-1; i >= 0; i--) + { + //edge number + edgenum = aasworld.edgeindex[face->firstedge + i]; + edge = &aasworld.edges[abs(edgenum)]; + VectorCopy(aasworld.vertexes[edge->v[edgenum < 0]], points[numpoints]); + numpoints++; + } //end for + } //end if + else + { + for (i = 0; i < face->numedges; i++) + { + //edge number + edgenum = aasworld.edgeindex[face->firstedge + i]; + edge = &aasworld.edges[abs(edgenum)]; + VectorCopy(aasworld.vertexes[edge->v[edgenum < 0]], points[numpoints]); + numpoints++; + } //end for + } //end else + AAS_ShowPolygon(color, numpoints, points); +} //end of the function AAS_ShowFacePolygon +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowArea(int areanum, int groundfacesonly) +{ + int areaedges[MAX_DEBUGLINES]; + int numareaedges, i, j, n, color = 0, line; + int facenum, edgenum; + aas_area_t *area; + aas_face_t *face; + aas_edge_t *edge; + + // + numareaedges = 0; + // + if (areanum < 0 || areanum >= aasworld.numareas) + { + botimport.Print(PRT_ERROR, "area %d out of range [0, %d]\n", + areanum, aasworld.numareas); + return; + } //end if + //pointer to the convex area + area = &aasworld.areas[areanum]; + //walk through the faces of the area + for (i = 0; i < area->numfaces; i++) + { + facenum = abs(aasworld.faceindex[area->firstface + i]); + //check if face number is in range + if (facenum >= aasworld.numfaces) + { + botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); + } //end if + face = &aasworld.faces[facenum]; + //ground faces only + if (groundfacesonly) + { + if (!(face->faceflags & (FACE_GROUND | FACE_LADDER))) continue; + } //end if + //walk through the edges of the face + for (j = 0; j < face->numedges; j++) + { + //edge number + edgenum = abs(aasworld.edgeindex[face->firstedge + j]); + //check if edge number is in range + if (edgenum >= aasworld.numedges) + { + botimport.Print(PRT_ERROR, "edgenum %d out of range\n", edgenum); + } //end if + //check if the edge is stored already + for (n = 0; n < numareaedges; n++) + { + if (areaedges[n] == edgenum) break; + } //end for + if (n == numareaedges && numareaedges < MAX_DEBUGLINES) + { + areaedges[numareaedges++] = edgenum; + } //end if + } //end for + //AAS_ShowFace(facenum); + } //end for + //draw all the edges + for (n = 0; n < numareaedges; n++) + { + for (line = 0; line < MAX_DEBUGLINES; line++) + { + if (!debuglines[line]) + { + debuglines[line] = botimport.DebugLineCreate(); + debuglinevisible[line] = qfalse; + numdebuglines++; + } //end if + if (!debuglinevisible[line]) + { + break; + } //end else + } //end for + if (line >= MAX_DEBUGLINES) return; + edge = &aasworld.edges[areaedges[n]]; + if (color == LINECOLOR_RED) color = LINECOLOR_BLUE; + else if (color == LINECOLOR_BLUE) color = LINECOLOR_GREEN; + else if (color == LINECOLOR_GREEN) color = LINECOLOR_YELLOW; + else color = LINECOLOR_RED; + botimport.DebugLineShow(debuglines[line], + aasworld.vertexes[edge->v[0]], + aasworld.vertexes[edge->v[1]], + color); + debuglinevisible[line] = qtrue; + } //end for*/ +} //end of the function AAS_ShowArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowAreaPolygons(int areanum, int color, int groundfacesonly) +{ + int i, facenum; + aas_area_t *area; + aas_face_t *face; + + // + if (areanum < 0 || areanum >= aasworld.numareas) + { + botimport.Print(PRT_ERROR, "area %d out of range [0, %d]\n", + areanum, aasworld.numareas); + return; + } //end if + //pointer to the convex area + area = &aasworld.areas[areanum]; + //walk through the faces of the area + for (i = 0; i < area->numfaces; i++) + { + facenum = abs(aasworld.faceindex[area->firstface + i]); + //check if face number is in range + if (facenum >= aasworld.numfaces) + { + botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); + } //end if + face = &aasworld.faces[facenum]; + //ground faces only + if (groundfacesonly) + { + if (!(face->faceflags & (FACE_GROUND | FACE_LADDER))) continue; + } //end if + AAS_ShowFacePolygon(facenum, color, face->frontarea != areanum); + } //end for +} //end of the function AAS_ShowAreaPolygons +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DrawCross(vec3_t origin, float size, int color) +{ + int i; + vec3_t start, end; + + for (i = 0; i < 3; i++) + { + VectorCopy(origin, start); + start[i] += size; + VectorCopy(origin, end); + end[i] -= size; + AAS_DebugLine(start, end, color); + } //end for +} //end of the function AAS_DrawCross +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_PrintTravelType(int traveltype) +{ +#ifdef DEBUG + char *str; + // + switch(traveltype & TRAVELTYPE_MASK) + { + case TRAVEL_INVALID: str = "TRAVEL_INVALID"; break; + case TRAVEL_WALK: str = "TRAVEL_WALK"; break; + case TRAVEL_CROUCH: str = "TRAVEL_CROUCH"; break; + case TRAVEL_BARRIERJUMP: str = "TRAVEL_BARRIERJUMP"; break; + case TRAVEL_JUMP: str = "TRAVEL_JUMP"; break; + case TRAVEL_LADDER: str = "TRAVEL_LADDER"; break; + case TRAVEL_WALKOFFLEDGE: str = "TRAVEL_WALKOFFLEDGE"; break; + case TRAVEL_SWIM: str = "TRAVEL_SWIM"; break; + case TRAVEL_WATERJUMP: str = "TRAVEL_WATERJUMP"; break; + case TRAVEL_TELEPORT: str = "TRAVEL_TELEPORT"; break; + case TRAVEL_ELEVATOR: str = "TRAVEL_ELEVATOR"; break; + case TRAVEL_ROCKETJUMP: str = "TRAVEL_ROCKETJUMP"; break; + case TRAVEL_BFGJUMP: str = "TRAVEL_BFGJUMP"; break; + case TRAVEL_GRAPPLEHOOK: str = "TRAVEL_GRAPPLEHOOK"; break; + case TRAVEL_JUMPPAD: str = "TRAVEL_JUMPPAD"; break; + case TRAVEL_FUNCBOB: str = "TRAVEL_FUNCBOB"; break; + default: str = "UNKNOWN TRAVEL TYPE"; break; + } //end switch + botimport.Print(PRT_MESSAGE, "%s", str); +#endif +} //end of the function AAS_PrintTravelType +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DrawArrow(vec3_t start, vec3_t end, int linecolor, int arrowcolor) +{ + vec3_t dir, cross, p1, p2, up = {0, 0, 1}; + float dot; + + VectorSubtract(end, start, dir); + VectorNormalize(dir); + dot = DotProduct(dir, up); + if (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0); + else CrossProduct(dir, up, cross); + + VectorMA(end, -6, dir, p1); + VectorCopy(p1, p2); + VectorMA(p1, 6, cross, p1); + VectorMA(p2, -6, cross, p2); + + AAS_DebugLine(start, end, linecolor); + AAS_DebugLine(p1, end, arrowcolor); + AAS_DebugLine(p2, end, arrowcolor); +} //end of the function AAS_DrawArrow +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowReachability(aas_reachability_t *reach) +{ + vec3_t dir, cmdmove, velocity; + float speed, zvel; + aas_clientmove_t move; + + AAS_ShowAreaPolygons(reach->areanum, 5, qtrue); + //AAS_ShowArea(reach->areanum, qtrue); + AAS_DrawArrow(reach->start, reach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW); + // + if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP || + (reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE) + { + AAS_HorizontalVelocityForJump(aassettings.phys_jumpvel, reach->start, reach->end, &speed); + // + VectorSubtract(reach->end, reach->start, dir); + dir[2] = 0; + VectorNormalize(dir); + //set the velocity + VectorScale(dir, speed, velocity); + //set the command movement + VectorClear(cmdmove); + cmdmove[2] = aassettings.phys_jumpvel; + // + AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, + velocity, cmdmove, 3, 30, 0.1f, + SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| + SE_ENTERLAVA|SE_HITGROUNDDAMAGE, 0, qtrue); + // + if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) + { + AAS_JumpReachRunStart(reach, dir); + AAS_DrawCross(dir, 4, LINECOLOR_BLUE); + } //end if + } //end if + else if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) + { + zvel = AAS_RocketJumpZVelocity(reach->start); + AAS_HorizontalVelocityForJump(zvel, reach->start, reach->end, &speed); + // + VectorSubtract(reach->end, reach->start, dir); + dir[2] = 0; + VectorNormalize(dir); + //get command movement + VectorScale(dir, speed, cmdmove); + VectorSet(velocity, 0, 0, zvel); + // + AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, + velocity, cmdmove, 30, 30, 0.1f, + SE_ENTERWATER|SE_ENTERSLIME| + SE_ENTERLAVA|SE_HITGROUNDDAMAGE| + SE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue); + } //end else if + else if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) + { + VectorSet(cmdmove, 0, 0, 0); + // + VectorSubtract(reach->end, reach->start, dir); + dir[2] = 0; + VectorNormalize(dir); + //set the velocity + //NOTE: the edgenum is the horizontal velocity + VectorScale(dir, reach->edgenum, velocity); + //NOTE: the facenum is the Z velocity + velocity[2] = reach->facenum; + // + AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, + velocity, cmdmove, 30, 30, 0.1f, + SE_ENTERWATER|SE_ENTERSLIME| + SE_ENTERLAVA|SE_HITGROUNDDAMAGE| + SE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue); + } //end else if +} //end of the function AAS_ShowReachability +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowReachableAreas(int areanum) +{ + aas_areasettings_t *settings; + static aas_reachability_t reach; + static int index, lastareanum; + static float lasttime; + + if (areanum != lastareanum) + { + index = 0; + lastareanum = areanum; + } //end if + settings = &aasworld.areasettings[areanum]; + // + if (!settings->numreachableareas) return; + // + if (index >= settings->numreachableareas) index = 0; + // + if (AAS_Time() - lasttime > 1.5) + { + Com_Memcpy(&reach, &aasworld.reachability[settings->firstreachablearea + index], sizeof(aas_reachability_t)); + index++; + lasttime = AAS_Time(); + AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); + botimport.Print(PRT_MESSAGE, "\n"); + } //end if + AAS_ShowReachability(&reach); +} //end of the function ShowReachableAreas + +void AAS_FloodAreas_r(int areanum, int cluster, int *done) +{ + int nextareanum, i, facenum; + aas_area_t *area; + aas_face_t *face; + aas_areasettings_t *settings; + aas_reachability_t *reach; + + AAS_ShowAreaPolygons(areanum, 1, qtrue); + //pointer to the convex area + area = &aasworld.areas[areanum]; + settings = &aasworld.areasettings[areanum]; + //walk through the faces of the area + for (i = 0; i < area->numfaces; i++) + { + facenum = abs(aasworld.faceindex[area->firstface + i]); + face = &aasworld.faces[facenum]; + if (face->frontarea == areanum) + nextareanum = face->backarea; + else + nextareanum = face->frontarea; + if (!nextareanum) + continue; + if (done[nextareanum]) + continue; + done[nextareanum] = qtrue; + if (aasworld.areasettings[nextareanum].contents & AREACONTENTS_VIEWPORTAL) + continue; + if (AAS_AreaCluster(nextareanum) != cluster) + continue; + AAS_FloodAreas_r(nextareanum, cluster, done); + } //end for + // + for (i = 0; i < settings->numreachableareas; i++) + { + reach = &aasworld.reachability[settings->firstreachablearea + i]; + nextareanum = reach->areanum; + if (!nextareanum) + continue; + if (done[nextareanum]) + continue; + done[nextareanum] = qtrue; + if (aasworld.areasettings[nextareanum].contents & AREACONTENTS_VIEWPORTAL) + continue; + if (AAS_AreaCluster(nextareanum) != cluster) + continue; + /* + if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE) + { + AAS_DebugLine(reach->start, reach->end, 1); + } + */ + AAS_FloodAreas_r(nextareanum, cluster, done); + } +} + +void AAS_FloodAreas(vec3_t origin) +{ + int areanum, cluster, *done; + + done = (int *) GetClearedMemory(aasworld.numareas * sizeof(int)); + areanum = AAS_PointAreaNum(origin); + cluster = AAS_AreaCluster(areanum); + AAS_FloodAreas_r(areanum, cluster, done); +} diff --git a/reaction/engine/code/botlib/be_aas_debug.h b/reaction/engine/code/botlib/be_aas_debug.h new file mode 100644 index 00000000..008eeba6 --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_debug.h @@ -0,0 +1,62 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_debug.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_debug.h $ + * + *****************************************************************************/ + +//clear the shown debug lines +void AAS_ClearShownDebugLines(void); +// +void AAS_ClearShownPolygons(void); +//show a debug line +void AAS_DebugLine(vec3_t start, vec3_t end, int color); +//show a permenent line +void AAS_PermanentLine(vec3_t start, vec3_t end, int color); +//show a permanent cross +void AAS_DrawPermanentCross(vec3_t origin, float size, int color); +//draw a cross in the plane +void AAS_DrawPlaneCross(vec3_t point, vec3_t normal, float dist, int type, int color); +//show a bounding box +void AAS_ShowBoundingBox(vec3_t origin, vec3_t mins, vec3_t maxs); +//show a face +void AAS_ShowFace(int facenum); +//show an area +void AAS_ShowArea(int areanum, int groundfacesonly); +// +void AAS_ShowAreaPolygons(int areanum, int color, int groundfacesonly); +//draw a cros +void AAS_DrawCross(vec3_t origin, float size, int color); +//print the travel type +void AAS_PrintTravelType(int traveltype); +//draw an arrow +void AAS_DrawArrow(vec3_t start, vec3_t end, int linecolor, int arrowcolor); +//visualize the given reachability +void AAS_ShowReachability(struct aas_reachability_s *reach); +//show the reachable areas from the given area +void AAS_ShowReachableAreas(int areanum); + diff --git a/reaction/engine/code/botlib/be_aas_def.h b/reaction/engine/code/botlib/be_aas_def.h new file mode 100644 index 00000000..eb995d6c --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_def.h @@ -0,0 +1,306 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_def.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_def.h $ + * + *****************************************************************************/ + +//debugging on +#define AAS_DEBUG + +#define MAX_CLIENTS 64 +#define MAX_MODELS 256 // these are sent over the net as 8 bits +#define MAX_SOUNDS 256 // so they cannot be blindly increased +#define MAX_CONFIGSTRINGS 1024 + +#define CS_SCORES 32 +#define CS_MODELS (CS_SCORES+MAX_CLIENTS) +#define CS_SOUNDS (CS_MODELS+MAX_MODELS) + +#define DF_AASENTNUMBER(x) (x - aasworld.entities) +#define DF_NUMBERAASENT(x) (&aasworld.entities[x]) +#define DF_AASENTCLIENT(x) (x - aasworld.entities - 1) +#define DF_CLIENTAASENT(x) (&aasworld.entities[x + 1]) + +#ifndef MAX_PATH + #define MAX_PATH MAX_QPATH +#endif + +//string index (for model, sound and image index) +typedef struct aas_stringindex_s +{ + int numindexes; + char **index; +} aas_stringindex_t; + +//structure to link entities to areas and areas to entities +typedef struct aas_link_s +{ + int entnum; + int areanum; + struct aas_link_s *next_ent, *prev_ent; + struct aas_link_s *next_area, *prev_area; +} aas_link_t; + +//structure to link entities to leaves and leaves to entities +typedef struct bsp_link_s +{ + int entnum; + int leafnum; + struct bsp_link_s *next_ent, *prev_ent; + struct bsp_link_s *next_leaf, *prev_leaf; +} bsp_link_t; + +typedef struct bsp_entdata_s +{ + vec3_t origin; + vec3_t angles; + vec3_t absmins; + vec3_t absmaxs; + int solid; + int modelnum; +} bsp_entdata_t; + +//entity +typedef struct aas_entity_s +{ + //entity info + aas_entityinfo_t i; + //links into the AAS areas + aas_link_t *areas; + //links into the BSP leaves + bsp_link_t *leaves; +} aas_entity_t; + +typedef struct aas_settings_s +{ + vec3_t phys_gravitydirection; + float phys_friction; + float phys_stopspeed; + float phys_gravity; + float phys_waterfriction; + float phys_watergravity; + float phys_maxvelocity; + float phys_maxwalkvelocity; + float phys_maxcrouchvelocity; + float phys_maxswimvelocity; + float phys_walkaccelerate; + float phys_airaccelerate; + float phys_swimaccelerate; + float phys_maxstep; + float phys_maxsteepness; + float phys_maxwaterjump; + float phys_maxbarrier; + float phys_jumpvel; + float phys_falldelta5; + float phys_falldelta10; + float rs_waterjump; + float rs_teleport; + float rs_barrierjump; + float rs_startcrouch; + float rs_startgrapple; + float rs_startwalkoffledge; + float rs_startjump; + float rs_rocketjump; + float rs_bfgjump; + float rs_jumppad; + float rs_aircontrolledjumppad; + float rs_funcbob; + float rs_startelevator; + float rs_falldamage5; + float rs_falldamage10; + float rs_maxfallheight; + float rs_maxjumpfallheight; +} aas_settings_t; + +#define CACHETYPE_PORTAL 0 +#define CACHETYPE_AREA 1 + +//routing cache +typedef struct aas_routingcache_s +{ + byte type; //portal or area cache + float time; //last time accessed or updated + int size; //size of the routing cache + int cluster; //cluster the cache is for + int areanum; //area the cache is created for + vec3_t origin; //origin within the area + float starttraveltime; //travel time to start with + int travelflags; //combinations of the travel flags + struct aas_routingcache_s *prev, *next; + struct aas_routingcache_s *time_prev, *time_next; + unsigned char *reachabilities; //reachabilities used for routing + unsigned short int traveltimes[1]; //travel time for every area (variable sized) +} aas_routingcache_t; + +//fields for the routing algorithm +typedef struct aas_routingupdate_s +{ + int cluster; + int areanum; //area number of the update + vec3_t start; //start point the area was entered + unsigned short int tmptraveltime; //temporary travel time + unsigned short int *areatraveltimes; //travel times within the area + qboolean inlist; //true if the update is in the list + struct aas_routingupdate_s *next; + struct aas_routingupdate_s *prev; +} aas_routingupdate_t; + +//reversed reachability link +typedef struct aas_reversedlink_s +{ + int linknum; //the aas_areareachability_t + int areanum; //reachable from this area + struct aas_reversedlink_s *next; //next link +} aas_reversedlink_t; + +//reversed area reachability +typedef struct aas_reversedreachability_s +{ + int numlinks; + aas_reversedlink_t *first; +} aas_reversedreachability_t; + +//areas a reachability goes through +typedef struct aas_reachabilityareas_s +{ + int firstarea, numareas; +} aas_reachabilityareas_t; + +typedef struct aas_s +{ + int loaded; //true when an AAS file is loaded + int initialized; //true when AAS has been initialized + int savefile; //set true when file should be saved + int bspchecksum; + //current time + float time; + int numframes; + //name of the aas file + char filename[MAX_PATH]; + char mapname[MAX_PATH]; + //bounding boxes + int numbboxes; + aas_bbox_t *bboxes; + //vertexes + int numvertexes; + aas_vertex_t *vertexes; + //planes + int numplanes; + aas_plane_t *planes; + //edges + int numedges; + aas_edge_t *edges; + //edge index + int edgeindexsize; + aas_edgeindex_t *edgeindex; + //faces + int numfaces; + aas_face_t *faces; + //face index + int faceindexsize; + aas_faceindex_t *faceindex; + //convex areas + int numareas; + aas_area_t *areas; + //convex area settings + int numareasettings; + aas_areasettings_t *areasettings; + //reachablity list + int reachabilitysize; + aas_reachability_t *reachability; + //nodes of the bsp tree + int numnodes; + aas_node_t *nodes; + //cluster portals + int numportals; + aas_portal_t *portals; + //cluster portal index + int portalindexsize; + aas_portalindex_t *portalindex; + //clusters + int numclusters; + aas_cluster_t *clusters; + // + int numreachabilityareas; + float reachabilitytime; + //enities linked in the areas + aas_link_t *linkheap; //heap with link structures + int linkheapsize; //size of the link heap + aas_link_t *freelinks; //first free link + aas_link_t **arealinkedentities; //entities linked into areas + //entities + int maxentities; + int maxclients; + aas_entity_t *entities; + //string indexes + char *configstrings[MAX_CONFIGSTRINGS]; + int indexessetup; + //index to retrieve travel flag for a travel type + int travelflagfortype[MAX_TRAVELTYPES]; + //travel flags for each area based on contents + int *areacontentstravelflags; + //routing update + aas_routingupdate_t *areaupdate; + aas_routingupdate_t *portalupdate; + //number of routing updates during a frame (reset every frame) + int frameroutingupdates; + //reversed reachability links + aas_reversedreachability_t *reversedreachability; + //travel times within the areas + unsigned short ***areatraveltimes; + //array of size numclusters with cluster cache + aas_routingcache_t ***clusterareacache; + aas_routingcache_t **portalcache; + //cache list sorted on time + aas_routingcache_t *oldestcache; // start of cache list sorted on time + aas_routingcache_t *newestcache; // end of cache list sorted on time + //maximum travel time through portal areas + int *portalmaxtraveltimes; + //areas the reachabilities go through + int *reachabilityareaindex; + aas_reachabilityareas_t *reachabilityareas; +} aas_t; + +#define AASINTERN + +#ifndef BSPCINCLUDE + +#include "be_aas_main.h" +#include "be_aas_entity.h" +#include "be_aas_sample.h" +#include "be_aas_cluster.h" +#include "be_aas_reach.h" +#include "be_aas_route.h" +#include "be_aas_routealt.h" +#include "be_aas_debug.h" +#include "be_aas_file.h" +#include "be_aas_optimize.h" +#include "be_aas_bsp.h" +#include "be_aas_move.h" + +#endif //BSPCINCLUDE diff --git a/reaction/engine/code/botlib/be_aas_entity.c b/reaction/engine/code/botlib/be_aas_entity.c new file mode 100644 index 00000000..02699bde --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_entity.c @@ -0,0 +1,437 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_entity.c + * + * desc: AAS entities + * + * $Archive: /MissionPack/code/botlib/be_aas_entity.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_utils.h" +#include "l_log.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_aas_def.h" + +#define MASK_SOLID CONTENTS_PLAYERCLIP + +//FIXME: these might change +enum { + ET_GENERAL, + ET_PLAYER, + ET_ITEM, + ET_MISSILE, + ET_MOVER +}; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_UpdateEntity(int entnum, bot_entitystate_t *state) +{ + int relink; + aas_entity_t *ent; + vec3_t absmins, absmaxs; + + if (!aasworld.loaded) + { + botimport.Print(PRT_MESSAGE, "AAS_UpdateEntity: not loaded\n"); + return BLERR_NOAASFILE; + } //end if + + ent = &aasworld.entities[entnum]; + + if (!state) { + //unlink the entity + AAS_UnlinkFromAreas(ent->areas); + //unlink the entity from the BSP leaves + AAS_UnlinkFromBSPLeaves(ent->leaves); + // + ent->areas = NULL; + // + ent->leaves = NULL; + return BLERR_NOERROR; + } + + ent->i.update_time = AAS_Time() - ent->i.ltime; + ent->i.type = state->type; + ent->i.flags = state->flags; + ent->i.ltime = AAS_Time(); + VectorCopy(ent->i.origin, ent->i.lastvisorigin); + VectorCopy(state->old_origin, ent->i.old_origin); + ent->i.solid = state->solid; + ent->i.groundent = state->groundent; + ent->i.modelindex = state->modelindex; + ent->i.modelindex2 = state->modelindex2; + ent->i.frame = state->frame; + ent->i.event = state->event; + ent->i.eventParm = state->eventParm; + ent->i.powerups = state->powerups; + ent->i.weapon = state->weapon; + ent->i.legsAnim = state->legsAnim; + ent->i.torsoAnim = state->torsoAnim; + //number of the entity + ent->i.number = entnum; + //updated so set valid flag + ent->i.valid = qtrue; + //link everything the first frame + if (aasworld.numframes == 1) relink = qtrue; + else relink = qfalse; + // + if (ent->i.solid == SOLID_BSP) + { + //if the angles of the model changed + if (!VectorCompare(state->angles, ent->i.angles)) + { + VectorCopy(state->angles, ent->i.angles); + relink = qtrue; + } //end if + //get the mins and maxs of the model + //FIXME: rotate mins and maxs + AAS_BSPModelMinsMaxsOrigin(ent->i.modelindex, ent->i.angles, ent->i.mins, ent->i.maxs, NULL); + } //end if + else if (ent->i.solid == SOLID_BBOX) + { + //if the bounding box size changed + if (!VectorCompare(state->mins, ent->i.mins) || + !VectorCompare(state->maxs, ent->i.maxs)) + { + VectorCopy(state->mins, ent->i.mins); + VectorCopy(state->maxs, ent->i.maxs); + relink = qtrue; + } //end if + VectorCopy(state->angles, ent->i.angles); + } //end if + //if the origin changed + if (!VectorCompare(state->origin, ent->i.origin)) + { + VectorCopy(state->origin, ent->i.origin); + relink = qtrue; + } //end if + //if the entity should be relinked + if (relink) + { + //don't link the world model + if (entnum != ENTITYNUM_WORLD) + { + //absolute mins and maxs + VectorAdd(ent->i.mins, ent->i.origin, absmins); + VectorAdd(ent->i.maxs, ent->i.origin, absmaxs); + //unlink the entity + AAS_UnlinkFromAreas(ent->areas); + //relink the entity to the AAS areas (use the larges bbox) + ent->areas = AAS_LinkEntityClientBBox(absmins, absmaxs, entnum, PRESENCE_NORMAL); + //unlink the entity from the BSP leaves + AAS_UnlinkFromBSPLeaves(ent->leaves); + //link the entity to the world BSP tree + ent->leaves = AAS_BSPLinkEntity(absmins, absmaxs, entnum, 0); + } //end if + } //end if + return BLERR_NOERROR; +} //end of the function AAS_UpdateEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_EntityInfo(int entnum, aas_entityinfo_t *info) +{ + if (!aasworld.initialized) + { + botimport.Print(PRT_FATAL, "AAS_EntityInfo: aasworld not initialized\n"); + Com_Memset(info, 0, sizeof(aas_entityinfo_t)); + return; + } //end if + + if (entnum < 0 || entnum >= aasworld.maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntityInfo: entnum %d out of range\n", entnum); + Com_Memset(info, 0, sizeof(aas_entityinfo_t)); + return; + } //end if + + Com_Memcpy(info, &aasworld.entities[entnum].i, sizeof(aas_entityinfo_t)); +} //end of the function AAS_EntityInfo +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_EntityOrigin(int entnum, vec3_t origin) +{ + if (entnum < 0 || entnum >= aasworld.maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntityOrigin: entnum %d out of range\n", entnum); + VectorClear(origin); + return; + } //end if + + VectorCopy(aasworld.entities[entnum].i.origin, origin); +} //end of the function AAS_EntityOrigin +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_EntityModelindex(int entnum) +{ + if (entnum < 0 || entnum >= aasworld.maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntityModelindex: entnum %d out of range\n", entnum); + return 0; + } //end if + return aasworld.entities[entnum].i.modelindex; +} //end of the function AAS_EntityModelindex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_EntityType(int entnum) +{ + if (!aasworld.initialized) return 0; + + if (entnum < 0 || entnum >= aasworld.maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntityType: entnum %d out of range\n", entnum); + return 0; + } //end if + return aasworld.entities[entnum].i.type; +} //end of the AAS_EntityType +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_EntityModelNum(int entnum) +{ + if (!aasworld.initialized) return 0; + + if (entnum < 0 || entnum >= aasworld.maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntityModelNum: entnum %d out of range\n", entnum); + return 0; + } //end if + return aasworld.entities[entnum].i.modelindex; +} //end of the function AAS_EntityModelNum +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_OriginOfMoverWithModelNum(int modelnum, vec3_t origin) +{ + int i; + aas_entity_t *ent; + + for (i = 0; i < aasworld.maxentities; i++) + { + ent = &aasworld.entities[i]; + if (ent->i.type == ET_MOVER) + { + if (ent->i.modelindex == modelnum) + { + VectorCopy(ent->i.origin, origin); + return qtrue; + } //end if + } //end if + } //end for + return qfalse; +} //end of the function AAS_OriginOfMoverWithModelNum +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_EntitySize(int entnum, vec3_t mins, vec3_t maxs) +{ + aas_entity_t *ent; + + if (!aasworld.initialized) return; + + if (entnum < 0 || entnum >= aasworld.maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntitySize: entnum %d out of range\n", entnum); + return; + } //end if + + ent = &aasworld.entities[entnum]; + VectorCopy(ent->i.mins, mins); + VectorCopy(ent->i.maxs, maxs); +} //end of the function AAS_EntitySize +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_EntityBSPData(int entnum, bsp_entdata_t *entdata) +{ + aas_entity_t *ent; + + ent = &aasworld.entities[entnum]; + VectorCopy(ent->i.origin, entdata->origin); + VectorCopy(ent->i.angles, entdata->angles); + VectorAdd(ent->i.origin, ent->i.mins, entdata->absmins); + VectorAdd(ent->i.origin, ent->i.maxs, entdata->absmaxs); + entdata->solid = ent->i.solid; + entdata->modelnum = ent->i.modelindex - 1; +} //end of the function AAS_EntityBSPData +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ResetEntityLinks(void) +{ + int i; + for (i = 0; i < aasworld.maxentities; i++) + { + aasworld.entities[i].areas = NULL; + aasworld.entities[i].leaves = NULL; + } //end for +} //end of the function AAS_ResetEntityLinks +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InvalidateEntities(void) +{ + int i; + for (i = 0; i < aasworld.maxentities; i++) + { + aasworld.entities[i].i.valid = qfalse; + aasworld.entities[i].i.number = i; + } //end for +} //end of the function AAS_InvalidateEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_UnlinkInvalidEntities(void) +{ + int i; + aas_entity_t *ent; + + for (i = 0; i < aasworld.maxentities; i++) + { + ent = &aasworld.entities[i]; + if (!ent->i.valid) + { + AAS_UnlinkFromAreas( ent->areas ); + ent->areas = NULL; + AAS_UnlinkFromBSPLeaves( ent->leaves ); + ent->leaves = NULL; + } //end for + } //end for +} //end of the function AAS_UnlinkInvalidEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NearestEntity(vec3_t origin, int modelindex) +{ + int i, bestentnum; + float dist, bestdist; + aas_entity_t *ent; + vec3_t dir; + + bestentnum = 0; + bestdist = 99999; + for (i = 0; i < aasworld.maxentities; i++) + { + ent = &aasworld.entities[i]; + if (ent->i.modelindex != modelindex) continue; + VectorSubtract(ent->i.origin, origin, dir); + if (abs(dir[0]) < 40) + { + if (abs(dir[1]) < 40) + { + dist = VectorLength(dir); + if (dist < bestdist) + { + bestdist = dist; + bestentnum = i; + } //end if + } //end if + } //end if + } //end for + return bestentnum; +} //end of the function AAS_NearestEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BestReachableEntityArea(int entnum) +{ + aas_entity_t *ent; + + ent = &aasworld.entities[entnum]; + return AAS_BestReachableLinkArea(ent->areas); +} //end of the function AAS_BestReachableEntityArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NextEntity(int entnum) +{ + if (!aasworld.loaded) return 0; + + if (entnum < 0) entnum = -1; + while(++entnum < aasworld.maxentities) + { + if (aasworld.entities[entnum].i.valid) return entnum; + } //end while + return 0; +} //end of the function AAS_NextEntity diff --git a/reaction/engine/code/botlib/be_aas_entity.h b/reaction/engine/code/botlib/be_aas_entity.h new file mode 100644 index 00000000..01ec54c3 --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_entity.h @@ -0,0 +1,63 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_entity.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_entity.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +//invalidates all entity infos +void AAS_InvalidateEntities(void); +//unlink not updated entities +void AAS_UnlinkInvalidEntities(void); +//resets the entity AAS and BSP links (sets areas and leaves pointers to NULL) +void AAS_ResetEntityLinks(void); +//updates an entity +int AAS_UpdateEntity(int ent, bot_entitystate_t *state); +//gives the entity data used for collision detection +void AAS_EntityBSPData(int entnum, bsp_entdata_t *entdata); +#endif //AASINTERN + +//returns the size of the entity bounding box in mins and maxs +void AAS_EntitySize(int entnum, vec3_t mins, vec3_t maxs); +//returns the BSP model number of the entity +int AAS_EntityModelNum(int entnum); +//returns the origin of an entity with the given model number +int AAS_OriginOfMoverWithModelNum(int modelnum, vec3_t origin); +//returns the best reachable area the entity is situated in +int AAS_BestReachableEntityArea(int entnum); +//returns the info of the given entity +void AAS_EntityInfo(int entnum, aas_entityinfo_t *info); +//returns the next entity +int AAS_NextEntity(int entnum); +//returns the origin of the entity +void AAS_EntityOrigin(int entnum, vec3_t origin); +//returns the entity type +int AAS_EntityType(int entnum); +//returns the model index of the entity +int AAS_EntityModelindex(int entnum); + diff --git a/reaction/engine/code/botlib/be_aas_file.c b/reaction/engine/code/botlib/be_aas_file.c new file mode 100644 index 00000000..9395b1d3 --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_file.c @@ -0,0 +1,582 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_file.c + * + * desc: AAS file loading/writing + * + * $Archive: /MissionPack/code/botlib/be_aas_file.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_libvar.h" +#include "l_utils.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_aas_def.h" + +//#define AASFILEDEBUG + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SwapAASData(void) +{ + int i, j; + //bounding boxes + for (i = 0; i < aasworld.numbboxes; i++) + { + aasworld.bboxes[i].presencetype = LittleLong(aasworld.bboxes[i].presencetype); + aasworld.bboxes[i].flags = LittleLong(aasworld.bboxes[i].flags); + for (j = 0; j < 3; j++) + { + aasworld.bboxes[i].mins[j] = LittleLong(aasworld.bboxes[i].mins[j]); + aasworld.bboxes[i].maxs[j] = LittleLong(aasworld.bboxes[i].maxs[j]); + } //end for + } //end for + //vertexes + for (i = 0; i < aasworld.numvertexes; i++) + { + for (j = 0; j < 3; j++) + aasworld.vertexes[i][j] = LittleFloat(aasworld.vertexes[i][j]); + } //end for + //planes + for (i = 0; i < aasworld.numplanes; i++) + { + for (j = 0; j < 3; j++) + aasworld.planes[i].normal[j] = LittleFloat(aasworld.planes[i].normal[j]); + aasworld.planes[i].dist = LittleFloat(aasworld.planes[i].dist); + aasworld.planes[i].type = LittleLong(aasworld.planes[i].type); + } //end for + //edges + for (i = 0; i < aasworld.numedges; i++) + { + aasworld.edges[i].v[0] = LittleLong(aasworld.edges[i].v[0]); + aasworld.edges[i].v[1] = LittleLong(aasworld.edges[i].v[1]); + } //end for + //edgeindex + for (i = 0; i < aasworld.edgeindexsize; i++) + { + aasworld.edgeindex[i] = LittleLong(aasworld.edgeindex[i]); + } //end for + //faces + for (i = 0; i < aasworld.numfaces; i++) + { + aasworld.faces[i].planenum = LittleLong(aasworld.faces[i].planenum); + aasworld.faces[i].faceflags = LittleLong(aasworld.faces[i].faceflags); + aasworld.faces[i].numedges = LittleLong(aasworld.faces[i].numedges); + aasworld.faces[i].firstedge = LittleLong(aasworld.faces[i].firstedge); + aasworld.faces[i].frontarea = LittleLong(aasworld.faces[i].frontarea); + aasworld.faces[i].backarea = LittleLong(aasworld.faces[i].backarea); + } //end for + //face index + for (i = 0; i < aasworld.faceindexsize; i++) + { + aasworld.faceindex[i] = LittleLong(aasworld.faceindex[i]); + } //end for + //convex areas + for (i = 0; i < aasworld.numareas; i++) + { + aasworld.areas[i].areanum = LittleLong(aasworld.areas[i].areanum); + aasworld.areas[i].numfaces = LittleLong(aasworld.areas[i].numfaces); + aasworld.areas[i].firstface = LittleLong(aasworld.areas[i].firstface); + for (j = 0; j < 3; j++) + { + aasworld.areas[i].mins[j] = LittleFloat(aasworld.areas[i].mins[j]); + aasworld.areas[i].maxs[j] = LittleFloat(aasworld.areas[i].maxs[j]); + aasworld.areas[i].center[j] = LittleFloat(aasworld.areas[i].center[j]); + } //end for + } //end for + //area settings + for (i = 0; i < aasworld.numareasettings; i++) + { + aasworld.areasettings[i].contents = LittleLong(aasworld.areasettings[i].contents); + aasworld.areasettings[i].areaflags = LittleLong(aasworld.areasettings[i].areaflags); + aasworld.areasettings[i].presencetype = LittleLong(aasworld.areasettings[i].presencetype); + aasworld.areasettings[i].cluster = LittleLong(aasworld.areasettings[i].cluster); + aasworld.areasettings[i].clusterareanum = LittleLong(aasworld.areasettings[i].clusterareanum); + aasworld.areasettings[i].numreachableareas = LittleLong(aasworld.areasettings[i].numreachableareas); + aasworld.areasettings[i].firstreachablearea = LittleLong(aasworld.areasettings[i].firstreachablearea); + } //end for + //area reachability + for (i = 0; i < aasworld.reachabilitysize; i++) + { + aasworld.reachability[i].areanum = LittleLong(aasworld.reachability[i].areanum); + aasworld.reachability[i].facenum = LittleLong(aasworld.reachability[i].facenum); + aasworld.reachability[i].edgenum = LittleLong(aasworld.reachability[i].edgenum); + for (j = 0; j < 3; j++) + { + aasworld.reachability[i].start[j] = LittleFloat(aasworld.reachability[i].start[j]); + aasworld.reachability[i].end[j] = LittleFloat(aasworld.reachability[i].end[j]); + } //end for + aasworld.reachability[i].traveltype = LittleLong(aasworld.reachability[i].traveltype); + aasworld.reachability[i].traveltime = LittleShort(aasworld.reachability[i].traveltime); + } //end for + //nodes + for (i = 0; i < aasworld.numnodes; i++) + { + aasworld.nodes[i].planenum = LittleLong(aasworld.nodes[i].planenum); + aasworld.nodes[i].children[0] = LittleLong(aasworld.nodes[i].children[0]); + aasworld.nodes[i].children[1] = LittleLong(aasworld.nodes[i].children[1]); + } //end for + //cluster portals + for (i = 0; i < aasworld.numportals; i++) + { + aasworld.portals[i].areanum = LittleLong(aasworld.portals[i].areanum); + aasworld.portals[i].frontcluster = LittleLong(aasworld.portals[i].frontcluster); + aasworld.portals[i].backcluster = LittleLong(aasworld.portals[i].backcluster); + aasworld.portals[i].clusterareanum[0] = LittleLong(aasworld.portals[i].clusterareanum[0]); + aasworld.portals[i].clusterareanum[1] = LittleLong(aasworld.portals[i].clusterareanum[1]); + } //end for + //cluster portal index + for (i = 0; i < aasworld.portalindexsize; i++) + { + aasworld.portalindex[i] = LittleLong(aasworld.portalindex[i]); + } //end for + //cluster + for (i = 0; i < aasworld.numclusters; i++) + { + aasworld.clusters[i].numareas = LittleLong(aasworld.clusters[i].numareas); + aasworld.clusters[i].numreachabilityareas = LittleLong(aasworld.clusters[i].numreachabilityareas); + aasworld.clusters[i].numportals = LittleLong(aasworld.clusters[i].numportals); + aasworld.clusters[i].firstportal = LittleLong(aasworld.clusters[i].firstportal); + } //end for +} //end of the function AAS_SwapAASData +//=========================================================================== +// dump the current loaded aas file +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DumpAASData(void) +{ + aasworld.numbboxes = 0; + if (aasworld.bboxes) FreeMemory(aasworld.bboxes); + aasworld.bboxes = NULL; + aasworld.numvertexes = 0; + if (aasworld.vertexes) FreeMemory(aasworld.vertexes); + aasworld.vertexes = NULL; + aasworld.numplanes = 0; + if (aasworld.planes) FreeMemory(aasworld.planes); + aasworld.planes = NULL; + aasworld.numedges = 0; + if (aasworld.edges) FreeMemory(aasworld.edges); + aasworld.edges = NULL; + aasworld.edgeindexsize = 0; + if (aasworld.edgeindex) FreeMemory(aasworld.edgeindex); + aasworld.edgeindex = NULL; + aasworld.numfaces = 0; + if (aasworld.faces) FreeMemory(aasworld.faces); + aasworld.faces = NULL; + aasworld.faceindexsize = 0; + if (aasworld.faceindex) FreeMemory(aasworld.faceindex); + aasworld.faceindex = NULL; + aasworld.numareas = 0; + if (aasworld.areas) FreeMemory(aasworld.areas); + aasworld.areas = NULL; + aasworld.numareasettings = 0; + if (aasworld.areasettings) FreeMemory(aasworld.areasettings); + aasworld.areasettings = NULL; + aasworld.reachabilitysize = 0; + if (aasworld.reachability) FreeMemory(aasworld.reachability); + aasworld.reachability = NULL; + aasworld.numnodes = 0; + if (aasworld.nodes) FreeMemory(aasworld.nodes); + aasworld.nodes = NULL; + aasworld.numportals = 0; + if (aasworld.portals) FreeMemory(aasworld.portals); + aasworld.portals = NULL; + aasworld.numportals = 0; + if (aasworld.portalindex) FreeMemory(aasworld.portalindex); + aasworld.portalindex = NULL; + aasworld.portalindexsize = 0; + if (aasworld.clusters) FreeMemory(aasworld.clusters); + aasworld.clusters = NULL; + aasworld.numclusters = 0; + // + aasworld.loaded = qfalse; + aasworld.initialized = qfalse; + aasworld.savefile = qfalse; +} //end of the function AAS_DumpAASData +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef AASFILEDEBUG +void AAS_FileInfo(void) +{ + int i, n, optimized; + + botimport.Print(PRT_MESSAGE, "version = %d\n", AASVERSION); + botimport.Print(PRT_MESSAGE, "numvertexes = %d\n", aasworld.numvertexes); + botimport.Print(PRT_MESSAGE, "numplanes = %d\n", aasworld.numplanes); + botimport.Print(PRT_MESSAGE, "numedges = %d\n", aasworld.numedges); + botimport.Print(PRT_MESSAGE, "edgeindexsize = %d\n", aasworld.edgeindexsize); + botimport.Print(PRT_MESSAGE, "numfaces = %d\n", aasworld.numfaces); + botimport.Print(PRT_MESSAGE, "faceindexsize = %d\n", aasworld.faceindexsize); + botimport.Print(PRT_MESSAGE, "numareas = %d\n", aasworld.numareas); + botimport.Print(PRT_MESSAGE, "numareasettings = %d\n", aasworld.numareasettings); + botimport.Print(PRT_MESSAGE, "reachabilitysize = %d\n", aasworld.reachabilitysize); + botimport.Print(PRT_MESSAGE, "numnodes = %d\n", aasworld.numnodes); + botimport.Print(PRT_MESSAGE, "numportals = %d\n", aasworld.numportals); + botimport.Print(PRT_MESSAGE, "portalindexsize = %d\n", aasworld.portalindexsize); + botimport.Print(PRT_MESSAGE, "numclusters = %d\n", aasworld.numclusters); + // + for (n = 0, i = 0; i < aasworld.numareasettings; i++) + { + if (aasworld.areasettings[i].areaflags & AREA_GROUNDED) n++; + } //end for + botimport.Print(PRT_MESSAGE, "num grounded areas = %d\n", n); + // + botimport.Print(PRT_MESSAGE, "planes size %d bytes\n", aasworld.numplanes * sizeof(aas_plane_t)); + botimport.Print(PRT_MESSAGE, "areas size %d bytes\n", aasworld.numareas * sizeof(aas_area_t)); + botimport.Print(PRT_MESSAGE, "areasettings size %d bytes\n", aasworld.numareasettings * sizeof(aas_areasettings_t)); + botimport.Print(PRT_MESSAGE, "nodes size %d bytes\n", aasworld.numnodes * sizeof(aas_node_t)); + botimport.Print(PRT_MESSAGE, "reachability size %d bytes\n", aasworld.reachabilitysize * sizeof(aas_reachability_t)); + botimport.Print(PRT_MESSAGE, "portals size %d bytes\n", aasworld.numportals * sizeof(aas_portal_t)); + botimport.Print(PRT_MESSAGE, "clusters size %d bytes\n", aasworld.numclusters * sizeof(aas_cluster_t)); + + optimized = aasworld.numplanes * sizeof(aas_plane_t) + + aasworld.numareas * sizeof(aas_area_t) + + aasworld.numareasettings * sizeof(aas_areasettings_t) + + aasworld.numnodes * sizeof(aas_node_t) + + aasworld.reachabilitysize * sizeof(aas_reachability_t) + + aasworld.numportals * sizeof(aas_portal_t) + + aasworld.numclusters * sizeof(aas_cluster_t); + botimport.Print(PRT_MESSAGE, "optimzed size %d KB\n", optimized >> 10); +} //end of the function AAS_FileInfo +#endif //AASFILEDEBUG +//=========================================================================== +// allocate memory and read a lump of a AAS file +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *AAS_LoadAASLump(fileHandle_t fp, int offset, int length, int *lastoffset, int size) +{ + char *buf; + // + if (!length) + { + //just alloc a dummy + return (char *) GetClearedHunkMemory(size+1); + } //end if + //seek to the data + if (offset != *lastoffset) + { + botimport.Print(PRT_WARNING, "AAS file not sequentially read\n"); + if (botimport.FS_Seek(fp, offset, FS_SEEK_SET)) + { + AAS_Error("can't seek to aas lump\n"); + AAS_DumpAASData(); + botimport.FS_FCloseFile(fp); + return NULL; + } //end if + } //end if + //allocate memory + buf = (char *) GetClearedHunkMemory(length+1); + //read the data + if (length) + { + botimport.FS_Read(buf, length, fp ); + *lastoffset += length; + } //end if + return buf; +} //end of the function AAS_LoadAASLump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DData(unsigned char *data, int size) +{ + int i; + + for (i = 0; i < size; i++) + { + data[i] ^= (unsigned char) i * 119; + } //end for +} //end of the function AAS_DData +//=========================================================================== +// load an aas file +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_LoadAASFile(char *filename) +{ + fileHandle_t fp; + aas_header_t header; + int offset, length, lastoffset; + + botimport.Print(PRT_MESSAGE, "trying to load %s\n", filename); + //dump current loaded aas file + AAS_DumpAASData(); + //open the file + botimport.FS_FOpenFile( filename, &fp, FS_READ ); + if (!fp) + { + AAS_Error("can't open %s\n", filename); + return BLERR_CANNOTOPENAASFILE; + } //end if + //read the header + botimport.FS_Read(&header, sizeof(aas_header_t), fp ); + lastoffset = sizeof(aas_header_t); + //check header identification + header.ident = LittleLong(header.ident); + if (header.ident != AASID) + { + AAS_Error("%s is not an AAS file\n", filename); + botimport.FS_FCloseFile(fp); + return BLERR_WRONGAASFILEID; + } //end if + //check the version + header.version = LittleLong(header.version); + // + if (header.version != AASVERSION_OLD && header.version != AASVERSION) + { + AAS_Error("aas file %s is version %i, not %i\n", filename, header.version, AASVERSION); + botimport.FS_FCloseFile(fp); + return BLERR_WRONGAASFILEVERSION; + } //end if + // + if (header.version == AASVERSION) + { + AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8); + } //end if + // + aasworld.bspchecksum = atoi(LibVarGetString( "sv_mapChecksum")); + if (LittleLong(header.bspchecksum) != aasworld.bspchecksum) + { + AAS_Error("aas file %s is out of date\n", filename); + botimport.FS_FCloseFile(fp); + return BLERR_WRONGAASFILEVERSION; + } //end if + //load the lumps: + //bounding boxes + offset = LittleLong(header.lumps[AASLUMP_BBOXES].fileofs); + length = LittleLong(header.lumps[AASLUMP_BBOXES].filelen); + aasworld.bboxes = (aas_bbox_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_bbox_t)); + aasworld.numbboxes = length / sizeof(aas_bbox_t); + if (aasworld.numbboxes && !aasworld.bboxes) return BLERR_CANNOTREADAASLUMP; + //vertexes + offset = LittleLong(header.lumps[AASLUMP_VERTEXES].fileofs); + length = LittleLong(header.lumps[AASLUMP_VERTEXES].filelen); + aasworld.vertexes = (aas_vertex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_vertex_t)); + aasworld.numvertexes = length / sizeof(aas_vertex_t); + if (aasworld.numvertexes && !aasworld.vertexes) return BLERR_CANNOTREADAASLUMP; + //planes + offset = LittleLong(header.lumps[AASLUMP_PLANES].fileofs); + length = LittleLong(header.lumps[AASLUMP_PLANES].filelen); + aasworld.planes = (aas_plane_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_plane_t)); + aasworld.numplanes = length / sizeof(aas_plane_t); + if (aasworld.numplanes && !aasworld.planes) return BLERR_CANNOTREADAASLUMP; + //edges + offset = LittleLong(header.lumps[AASLUMP_EDGES].fileofs); + length = LittleLong(header.lumps[AASLUMP_EDGES].filelen); + aasworld.edges = (aas_edge_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_edge_t)); + aasworld.numedges = length / sizeof(aas_edge_t); + if (aasworld.numedges && !aasworld.edges) return BLERR_CANNOTREADAASLUMP; + //edgeindex + offset = LittleLong(header.lumps[AASLUMP_EDGEINDEX].fileofs); + length = LittleLong(header.lumps[AASLUMP_EDGEINDEX].filelen); + aasworld.edgeindex = (aas_edgeindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_edgeindex_t)); + aasworld.edgeindexsize = length / sizeof(aas_edgeindex_t); + if (aasworld.edgeindexsize && !aasworld.edgeindex) return BLERR_CANNOTREADAASLUMP; + //faces + offset = LittleLong(header.lumps[AASLUMP_FACES].fileofs); + length = LittleLong(header.lumps[AASLUMP_FACES].filelen); + aasworld.faces = (aas_face_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_face_t)); + aasworld.numfaces = length / sizeof(aas_face_t); + if (aasworld.numfaces && !aasworld.faces) return BLERR_CANNOTREADAASLUMP; + //faceindex + offset = LittleLong(header.lumps[AASLUMP_FACEINDEX].fileofs); + length = LittleLong(header.lumps[AASLUMP_FACEINDEX].filelen); + aasworld.faceindex = (aas_faceindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_faceindex_t)); + aasworld.faceindexsize = length / sizeof(aas_faceindex_t); + if (aasworld.faceindexsize && !aasworld.faceindex) return BLERR_CANNOTREADAASLUMP; + //convex areas + offset = LittleLong(header.lumps[AASLUMP_AREAS].fileofs); + length = LittleLong(header.lumps[AASLUMP_AREAS].filelen); + aasworld.areas = (aas_area_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_area_t)); + aasworld.numareas = length / sizeof(aas_area_t); + if (aasworld.numareas && !aasworld.areas) return BLERR_CANNOTREADAASLUMP; + //area settings + offset = LittleLong(header.lumps[AASLUMP_AREASETTINGS].fileofs); + length = LittleLong(header.lumps[AASLUMP_AREASETTINGS].filelen); + aasworld.areasettings = (aas_areasettings_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_areasettings_t)); + aasworld.numareasettings = length / sizeof(aas_areasettings_t); + if (aasworld.numareasettings && !aasworld.areasettings) return BLERR_CANNOTREADAASLUMP; + //reachability list + offset = LittleLong(header.lumps[AASLUMP_REACHABILITY].fileofs); + length = LittleLong(header.lumps[AASLUMP_REACHABILITY].filelen); + aasworld.reachability = (aas_reachability_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_reachability_t)); + aasworld.reachabilitysize = length / sizeof(aas_reachability_t); + if (aasworld.reachabilitysize && !aasworld.reachability) return BLERR_CANNOTREADAASLUMP; + //nodes + offset = LittleLong(header.lumps[AASLUMP_NODES].fileofs); + length = LittleLong(header.lumps[AASLUMP_NODES].filelen); + aasworld.nodes = (aas_node_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_node_t)); + aasworld.numnodes = length / sizeof(aas_node_t); + if (aasworld.numnodes && !aasworld.nodes) return BLERR_CANNOTREADAASLUMP; + //cluster portals + offset = LittleLong(header.lumps[AASLUMP_PORTALS].fileofs); + length = LittleLong(header.lumps[AASLUMP_PORTALS].filelen); + aasworld.portals = (aas_portal_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_portal_t)); + aasworld.numportals = length / sizeof(aas_portal_t); + if (aasworld.numportals && !aasworld.portals) return BLERR_CANNOTREADAASLUMP; + //cluster portal index + offset = LittleLong(header.lumps[AASLUMP_PORTALINDEX].fileofs); + length = LittleLong(header.lumps[AASLUMP_PORTALINDEX].filelen); + aasworld.portalindex = (aas_portalindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_portalindex_t)); + aasworld.portalindexsize = length / sizeof(aas_portalindex_t); + if (aasworld.portalindexsize && !aasworld.portalindex) return BLERR_CANNOTREADAASLUMP; + //clusters + offset = LittleLong(header.lumps[AASLUMP_CLUSTERS].fileofs); + length = LittleLong(header.lumps[AASLUMP_CLUSTERS].filelen); + aasworld.clusters = (aas_cluster_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_cluster_t)); + aasworld.numclusters = length / sizeof(aas_cluster_t); + if (aasworld.numclusters && !aasworld.clusters) return BLERR_CANNOTREADAASLUMP; + //swap everything + AAS_SwapAASData(); + //aas file is loaded + aasworld.loaded = qtrue; + //close the file + botimport.FS_FCloseFile(fp); + // +#ifdef AASFILEDEBUG + AAS_FileInfo(); +#endif //AASFILEDEBUG + // + return BLERR_NOERROR; +} //end of the function AAS_LoadAASFile +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +static int AAS_WriteAASLump_offset; + +int AAS_WriteAASLump(fileHandle_t fp, aas_header_t *h, int lumpnum, void *data, int length) +{ + aas_lump_t *lump; + + lump = &h->lumps[lumpnum]; + + lump->fileofs = LittleLong(AAS_WriteAASLump_offset); //LittleLong(ftell(fp)); + lump->filelen = LittleLong(length); + + if (length > 0) + { + botimport.FS_Write(data, length, fp ); + } //end if + + AAS_WriteAASLump_offset += length; + + return qtrue; +} //end of the function AAS_WriteAASLump +//=========================================================================== +// aas data is useless after writing to file because it is byte swapped +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_WriteAASFile(char *filename) +{ + aas_header_t header; + fileHandle_t fp; + + botimport.Print(PRT_MESSAGE, "writing %s\n", filename); + //swap the aas data + AAS_SwapAASData(); + //initialize the file header + Com_Memset(&header, 0, sizeof(aas_header_t)); + header.ident = LittleLong(AASID); + header.version = LittleLong(AASVERSION); + header.bspchecksum = LittleLong(aasworld.bspchecksum); + //open a new file + botimport.FS_FOpenFile( filename, &fp, FS_WRITE ); + if (!fp) + { + botimport.Print(PRT_ERROR, "error opening %s\n", filename); + return qfalse; + } //end if + //write the header + botimport.FS_Write(&header, sizeof(aas_header_t), fp); + AAS_WriteAASLump_offset = sizeof(aas_header_t); + //add the data lumps to the file + if (!AAS_WriteAASLump(fp, &header, AASLUMP_BBOXES, aasworld.bboxes, + aasworld.numbboxes * sizeof(aas_bbox_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_VERTEXES, aasworld.vertexes, + aasworld.numvertexes * sizeof(aas_vertex_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_PLANES, aasworld.planes, + aasworld.numplanes * sizeof(aas_plane_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGES, aasworld.edges, + aasworld.numedges * sizeof(aas_edge_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGEINDEX, aasworld.edgeindex, + aasworld.edgeindexsize * sizeof(aas_edgeindex_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACES, aasworld.faces, + aasworld.numfaces * sizeof(aas_face_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACEINDEX, aasworld.faceindex, + aasworld.faceindexsize * sizeof(aas_faceindex_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREAS, aasworld.areas, + aasworld.numareas * sizeof(aas_area_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREASETTINGS, aasworld.areasettings, + aasworld.numareasettings * sizeof(aas_areasettings_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_REACHABILITY, aasworld.reachability, + aasworld.reachabilitysize * sizeof(aas_reachability_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_NODES, aasworld.nodes, + aasworld.numnodes * sizeof(aas_node_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALS, aasworld.portals, + aasworld.numportals * sizeof(aas_portal_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALINDEX, aasworld.portalindex, + aasworld.portalindexsize * sizeof(aas_portalindex_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_CLUSTERS, aasworld.clusters, + aasworld.numclusters * sizeof(aas_cluster_t))) return qfalse; + //rewrite the header with the added lumps + botimport.FS_Seek(fp, 0, FS_SEEK_SET); + AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8); + botimport.FS_Write(&header, sizeof(aas_header_t), fp); + //close the file + botimport.FS_FCloseFile(fp); + return qtrue; +} //end of the function AAS_WriteAASFile diff --git a/reaction/engine/code/botlib/be_aas_file.h b/reaction/engine/code/botlib/be_aas_file.h new file mode 100644 index 00000000..c2271fcf --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_file.h @@ -0,0 +1,42 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_file.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_file.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +//loads the AAS file with the given name +int AAS_LoadAASFile(char *filename); +//writes an AAS file with the given name +qboolean AAS_WriteAASFile(char *filename); +//dumps the loaded AAS data +void AAS_DumpAASData(void); +//print AAS file information +void AAS_FileInfo(void); +#endif //AASINTERN + diff --git a/reaction/engine/code/botlib/be_aas_funcs.h b/reaction/engine/code/botlib/be_aas_funcs.h new file mode 100644 index 00000000..87c7636f --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_funcs.h @@ -0,0 +1,47 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_funcs.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_funcs.h $ + * + *****************************************************************************/ + +#ifndef BSPCINCLUDE + +#include "be_aas_main.h" +#include "be_aas_entity.h" +#include "be_aas_sample.h" +#include "be_aas_cluster.h" +#include "be_aas_reach.h" +#include "be_aas_route.h" +#include "be_aas_routealt.h" +#include "be_aas_debug.h" +#include "be_aas_file.h" +#include "be_aas_optimize.h" +#include "be_aas_bsp.h" +#include "be_aas_move.h" + +#endif //BSPCINCLUDE diff --git a/reaction/engine/code/botlib/be_aas_main.c b/reaction/engine/code/botlib/be_aas_main.c new file mode 100644 index 00000000..0296b1a6 --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_main.c @@ -0,0 +1,429 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_main.c + * + * desc: AAS + * + * $Archive: /MissionPack/code/botlib/be_aas_main.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_libvar.h" +#include "l_utils.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_log.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_aas_def.h" + +aas_t aasworld; + +libvar_t *saveroutingcache; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void QDECL AAS_Error(char *fmt, ...) +{ + char str[1024]; + va_list arglist; + + va_start(arglist, fmt); + Q_vsnprintf(str, sizeof(str), fmt, arglist); + va_end(arglist); + botimport.Print(PRT_FATAL, "%s", str); +} //end of the function AAS_Error +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *AAS_StringFromIndex(char *indexname, char *stringindex[], int numindexes, int index) +{ + if (!aasworld.indexessetup) + { + botimport.Print(PRT_ERROR, "%s: index %d not setup\n", indexname, index); + return ""; + } //end if + if (index < 0 || index >= numindexes) + { + botimport.Print(PRT_ERROR, "%s: index %d out of range\n", indexname, index); + return ""; + } //end if + if (!stringindex[index]) + { + if (index) + { + botimport.Print(PRT_ERROR, "%s: reference to unused index %d\n", indexname, index); + } //end if + return ""; + } //end if + return stringindex[index]; +} //end of the function AAS_StringFromIndex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_IndexFromString(char *indexname, char *stringindex[], int numindexes, char *string) +{ + int i; + if (!aasworld.indexessetup) + { + botimport.Print(PRT_ERROR, "%s: index not setup \"%s\"\n", indexname, string); + return 0; + } //end if + for (i = 0; i < numindexes; i++) + { + if (!stringindex[i]) continue; + if (!Q_stricmp(stringindex[i], string)) return i; + } //end for + return 0; +} //end of the function AAS_IndexFromString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *AAS_ModelFromIndex(int index) +{ + return AAS_StringFromIndex("ModelFromIndex", &aasworld.configstrings[CS_MODELS], MAX_MODELS, index); +} //end of the function AAS_ModelFromIndex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_IndexFromModel(char *modelname) +{ + return AAS_IndexFromString("IndexFromModel", &aasworld.configstrings[CS_MODELS], MAX_MODELS, modelname); +} //end of the function AAS_IndexFromModel +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_UpdateStringIndexes(int numconfigstrings, char *configstrings[]) +{ + int i; + //set string pointers and copy the strings + for (i = 0; i < numconfigstrings; i++) + { + if (configstrings[i]) + { + //if (aasworld.configstrings[i]) FreeMemory(aasworld.configstrings[i]); + aasworld.configstrings[i] = (char *) GetMemory(strlen(configstrings[i]) + 1); + strcpy(aasworld.configstrings[i], configstrings[i]); + } //end if + } //end for + aasworld.indexessetup = qtrue; +} //end of the function AAS_UpdateStringIndexes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Loaded(void) +{ + return aasworld.loaded; +} //end of the function AAS_Loaded +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Initialized(void) +{ + return aasworld.initialized; +} //end of the function AAS_Initialized +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SetInitialized(void) +{ + aasworld.initialized = qtrue; + botimport.Print(PRT_MESSAGE, "AAS initialized.\n"); +#ifdef DEBUG + //create all the routing cache + //AAS_CreateAllRoutingCache(); + // + //AAS_RoutingInfo(); +#endif +} //end of the function AAS_SetInitialized +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ContinueInit(float time) +{ + //if no AAS file loaded + if (!aasworld.loaded) return; + //if AAS is already initialized + if (aasworld.initialized) return; + //calculate reachability, if not finished return + if (AAS_ContinueInitReachability(time)) return; + //initialize clustering for the new map + AAS_InitClustering(); + //if reachability has been calculated and an AAS file should be written + //or there is a forced data optimization + if (aasworld.savefile || ((int)LibVarGetValue("forcewrite"))) + { + //optimize the AAS data + if ((int)LibVarValue("aasoptimize", "0")) AAS_Optimize(); + //save the AAS file + if (AAS_WriteAASFile(aasworld.filename)) + { + botimport.Print(PRT_MESSAGE, "%s written succesfully\n", aasworld.filename); + } //end if + else + { + botimport.Print(PRT_ERROR, "couldn't write %s\n", aasworld.filename); + } //end else + } //end if + //initialize the routing + AAS_InitRouting(); + //at this point AAS is initialized + AAS_SetInitialized(); +} //end of the function AAS_ContinueInit +//=========================================================================== +// called at the start of every frame +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_StartFrame(float time) +{ + aasworld.time = time; + //unlink all entities that were not updated last frame + AAS_UnlinkInvalidEntities(); + //invalidate the entities + AAS_InvalidateEntities(); + //initialize AAS + AAS_ContinueInit(time); + // + aasworld.frameroutingupdates = 0; + // + if (bot_developer) + { + if (LibVarGetValue("showcacheupdates")) + { + AAS_RoutingInfo(); + LibVarSet("showcacheupdates", "0"); + } //end if + if (LibVarGetValue("showmemoryusage")) + { + PrintUsedMemorySize(); + LibVarSet("showmemoryusage", "0"); + } //end if + if (LibVarGetValue("memorydump")) + { + PrintMemoryLabels(); + LibVarSet("memorydump", "0"); + } //end if + } //end if + // + if (saveroutingcache->value) + { + AAS_WriteRouteCache(); + LibVarSet("saveroutingcache", "0"); + } //end if + // + aasworld.numframes++; + return BLERR_NOERROR; +} //end of the function AAS_StartFrame +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_Time(void) +{ + return aasworld.time; +} //end of the function AAS_Time +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj ) +{ + vec3_t pVec, vec; + + VectorSubtract( point, vStart, pVec ); + VectorSubtract( vEnd, vStart, vec ); + VectorNormalize( vec ); + // project onto the directional vector for this segment + VectorMA( vStart, DotProduct( pVec, vec ), vec, vProj ); +} //end of the function AAS_ProjectPointOntoVector +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_LoadFiles(const char *mapname) +{ + int errnum; + char aasfile[MAX_PATH]; +// char bspfile[MAX_PATH]; + + strcpy(aasworld.mapname, mapname); + //NOTE: first reset the entity links into the AAS areas and BSP leaves + // the AAS link heap and BSP link heap are reset after respectively the + // AAS file and BSP file are loaded + AAS_ResetEntityLinks(); + // load bsp info + AAS_LoadBSPFile(); + + //load the aas file + Com_sprintf(aasfile, MAX_PATH, "maps/%s.aas", mapname); + errnum = AAS_LoadAASFile(aasfile); + if (errnum != BLERR_NOERROR) + return errnum; + + botimport.Print(PRT_MESSAGE, "loaded %s\n", aasfile); + strncpy(aasworld.filename, aasfile, MAX_PATH); + return BLERR_NOERROR; +} //end of the function AAS_LoadFiles +//=========================================================================== +// called everytime a map changes +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_LoadMap(const char *mapname) +{ + int errnum; + + //if no mapname is provided then the string indexes are updated + if (!mapname) + { + return 0; + } //end if + // + aasworld.initialized = qfalse; + //NOTE: free the routing caches before loading a new map because + // to free the caches the old number of areas, number of clusters + // and number of areas in a clusters must be available + AAS_FreeRoutingCaches(); + //load the map + errnum = AAS_LoadFiles(mapname); + if (errnum != BLERR_NOERROR) + { + aasworld.loaded = qfalse; + return errnum; + } //end if + // + AAS_InitSettings(); + //initialize the AAS link heap for the new map + AAS_InitAASLinkHeap(); + //initialize the AAS linked entities for the new map + AAS_InitAASLinkedEntities(); + //initialize reachability for the new map + AAS_InitReachability(); + //initialize the alternative routing + AAS_InitAlternativeRouting(); + //everything went ok + return 0; +} //end of the function AAS_LoadMap +//=========================================================================== +// called when the library is first loaded +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Setup(void) +{ + aasworld.maxclients = (int) LibVarValue("maxclients", "128"); + aasworld.maxentities = (int) LibVarValue("maxentities", "1024"); + // as soon as it's set to 1 the routing cache will be saved + saveroutingcache = LibVar("saveroutingcache", "0"); + //allocate memory for the entities + if (aasworld.entities) FreeMemory(aasworld.entities); + aasworld.entities = (aas_entity_t *) GetClearedHunkMemory(aasworld.maxentities * sizeof(aas_entity_t)); + //invalidate all the entities + AAS_InvalidateEntities(); + //force some recalculations + //LibVarSet("forceclustering", "1"); //force clustering calculation + //LibVarSet("forcereachability", "1"); //force reachability calculation + aasworld.numframes = 0; + return BLERR_NOERROR; +} //end of the function AAS_Setup +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Shutdown(void) +{ + AAS_ShutdownAlternativeRouting(); + // + AAS_DumpBSPData(); + //free routing caches + AAS_FreeRoutingCaches(); + //free aas link heap + AAS_FreeAASLinkHeap(); + //free aas linked entities + AAS_FreeAASLinkedEntities(); + //free the aas data + AAS_DumpAASData(); + //free the entities + if (aasworld.entities) FreeMemory(aasworld.entities); + //clear the aasworld structure + Com_Memset(&aasworld, 0, sizeof(aas_t)); + //aas has not been initialized + aasworld.initialized = qfalse; + //NOTE: as soon as a new .bsp file is loaded the .bsp file memory is + // freed an reallocated, so there's no need to free that memory here + //print shutdown + botimport.Print(PRT_MESSAGE, "AAS shutdown.\n"); +} //end of the function AAS_Shutdown diff --git a/reaction/engine/code/botlib/be_aas_main.h b/reaction/engine/code/botlib/be_aas_main.h new file mode 100644 index 00000000..1d0bef10 --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_main.h @@ -0,0 +1,61 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_main.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_main.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN + +extern aas_t aasworld; + +//AAS error message +void QDECL AAS_Error(char *fmt, ...); +//set AAS initialized +void AAS_SetInitialized(void); +//setup AAS with the given number of entities and clients +int AAS_Setup(void); +//shutdown AAS +void AAS_Shutdown(void); +//start a new map +int AAS_LoadMap(const char *mapname); +//start a new time frame +int AAS_StartFrame(float time); +#endif //AASINTERN + +//returns true if AAS is initialized +int AAS_Initialized(void); +//returns true if the AAS file is loaded +int AAS_Loaded(void); +//returns the model name from the given index +char *AAS_ModelFromIndex(int index); +//returns the index from the given model name +int AAS_IndexFromModel(char *modelname); +//returns the current time +float AAS_Time(void); +// +void AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj ); diff --git a/reaction/engine/code/botlib/be_aas_move.c b/reaction/engine/code/botlib/be_aas_move.c new file mode 100644 index 00000000..fded262d --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_move.c @@ -0,0 +1,1101 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_move.c + * + * desc: AAS + * + * $Archive: /MissionPack/code/botlib/be_aas_move.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_libvar.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_aas_def.h" + +extern botlib_import_t botimport; + +aas_settings_t aassettings; + +//#define AAS_MOVE_DEBUG + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_DropToFloor(vec3_t origin, vec3_t mins, vec3_t maxs) +{ + vec3_t end; + bsp_trace_t trace; + + VectorCopy(origin, end); + end[2] -= 100; + trace = AAS_Trace(origin, mins, maxs, end, 0, CONTENTS_SOLID); + if (trace.startsolid) return qfalse; + VectorCopy(trace.endpos, origin); + return qtrue; +} //end of the function AAS_DropToFloor +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitSettings(void) +{ + aassettings.phys_gravitydirection[0] = 0; + aassettings.phys_gravitydirection[1] = 0; + aassettings.phys_gravitydirection[2] = -1; + aassettings.phys_friction = LibVarValue("phys_friction", "6"); + aassettings.phys_stopspeed = LibVarValue("phys_stopspeed", "100"); + aassettings.phys_gravity = LibVarValue("phys_gravity", "800"); + aassettings.phys_waterfriction = LibVarValue("phys_waterfriction", "1"); + aassettings.phys_watergravity = LibVarValue("phys_watergravity", "400"); + aassettings.phys_maxvelocity = LibVarValue("phys_maxvelocity", "320"); + aassettings.phys_maxwalkvelocity = LibVarValue("phys_maxwalkvelocity", "320"); + aassettings.phys_maxcrouchvelocity = LibVarValue("phys_maxcrouchvelocity", "100"); + aassettings.phys_maxswimvelocity = LibVarValue("phys_maxswimvelocity", "150"); + aassettings.phys_walkaccelerate = LibVarValue("phys_walkaccelerate", "10"); + aassettings.phys_airaccelerate = LibVarValue("phys_airaccelerate", "1"); + aassettings.phys_swimaccelerate = LibVarValue("phys_swimaccelerate", "4"); + aassettings.phys_maxstep = LibVarValue("phys_maxstep", "19"); + aassettings.phys_maxsteepness = LibVarValue("phys_maxsteepness", "0.7"); + aassettings.phys_maxwaterjump = LibVarValue("phys_maxwaterjump", "18"); + aassettings.phys_maxbarrier = LibVarValue("phys_maxbarrier", "33"); + aassettings.phys_jumpvel = LibVarValue("phys_jumpvel", "270"); + aassettings.phys_falldelta5 = LibVarValue("phys_falldelta5", "40"); + aassettings.phys_falldelta10 = LibVarValue("phys_falldelta10", "60"); + aassettings.rs_waterjump = LibVarValue("rs_waterjump", "400"); + aassettings.rs_teleport = LibVarValue("rs_teleport", "50"); + aassettings.rs_barrierjump = LibVarValue("rs_barrierjump", "100"); + aassettings.rs_startcrouch = LibVarValue("rs_startcrouch", "300"); + aassettings.rs_startgrapple = LibVarValue("rs_startgrapple", "500"); + aassettings.rs_startwalkoffledge = LibVarValue("rs_startwalkoffledge", "70"); + aassettings.rs_startjump = LibVarValue("rs_startjump", "300"); + aassettings.rs_rocketjump = LibVarValue("rs_rocketjump", "500"); + aassettings.rs_bfgjump = LibVarValue("rs_bfgjump", "500"); + aassettings.rs_jumppad = LibVarValue("rs_jumppad", "250"); + aassettings.rs_aircontrolledjumppad = LibVarValue("rs_aircontrolledjumppad", "300"); + aassettings.rs_funcbob = LibVarValue("rs_funcbob", "300"); + aassettings.rs_startelevator = LibVarValue("rs_startelevator", "50"); + aassettings.rs_falldamage5 = LibVarValue("rs_falldamage5", "300"); + aassettings.rs_falldamage10 = LibVarValue("rs_falldamage10", "500"); + aassettings.rs_maxfallheight = LibVarValue("rs_maxfallheight", "0"); + aassettings.rs_maxjumpfallheight = LibVarValue("rs_maxjumpfallheight", "450"); +} //end of the function AAS_InitSettings +//=========================================================================== +// returns qtrue if the bot is against a ladder +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AgainstLadder(vec3_t origin) +{ + int areanum, i, facenum, side; + vec3_t org; + aas_plane_t *plane; + aas_face_t *face; + aas_area_t *area; + + VectorCopy(origin, org); + areanum = AAS_PointAreaNum(org); + if (!areanum) + { + org[0] += 1; + areanum = AAS_PointAreaNum(org); + if (!areanum) + { + org[1] += 1; + areanum = AAS_PointAreaNum(org); + if (!areanum) + { + org[0] -= 2; + areanum = AAS_PointAreaNum(org); + if (!areanum) + { + org[1] -= 2; + areanum = AAS_PointAreaNum(org); + } //end if + } //end if + } //end if + } //end if + //if in solid... wrrr shouldn't happen + if (!areanum) return qfalse; + //if not in a ladder area + if (!(aasworld.areasettings[areanum].areaflags & AREA_LADDER)) return qfalse; + //if a crouch only area + if (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qfalse; + // + area = &aasworld.areas[areanum]; + for (i = 0; i < area->numfaces; i++) + { + facenum = aasworld.faceindex[area->firstface + i]; + side = facenum < 0; + face = &aasworld.faces[abs(facenum)]; + //if the face isn't a ladder face + if (!(face->faceflags & FACE_LADDER)) continue; + //get the plane the face is in + plane = &aasworld.planes[face->planenum ^ side]; + //if the origin is pretty close to the plane + if (abs(DotProduct(plane->normal, origin) - plane->dist) < 3) + { + if (AAS_PointInsideFace(abs(facenum), origin, 0.1f)) return qtrue; + } //end if + } //end for + return qfalse; +} //end of the function AAS_AgainstLadder +//=========================================================================== +// returns qtrue if the bot is on the ground +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_OnGround(vec3_t origin, int presencetype, int passent) +{ + aas_trace_t trace; + vec3_t end, up = {0, 0, 1}; + aas_plane_t *plane; + + VectorCopy(origin, end); + end[2] -= 10; + + trace = AAS_TraceClientBBox(origin, end, presencetype, passent); + + //if in solid + if (trace.startsolid) return qfalse; + //if nothing hit at all + if (trace.fraction >= 1.0) return qfalse; + //if too far from the hit plane + if (origin[2] - trace.endpos[2] > 10) return qfalse; + //check if the plane isn't too steep + plane = AAS_PlaneFromNum(trace.planenum); + if (DotProduct(plane->normal, up) < aassettings.phys_maxsteepness) return qfalse; + //the bot is on the ground + return qtrue; +} //end of the function AAS_OnGround +//=========================================================================== +// returns qtrue if a bot at the given position is swimming +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Swimming(vec3_t origin) +{ + vec3_t testorg; + + VectorCopy(origin, testorg); + testorg[2] -= 2; + if (AAS_PointContents(testorg) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) return qtrue; + return qfalse; +} //end of the function AAS_Swimming +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +static vec3_t VEC_UP = {0, -1, 0}; +static vec3_t MOVEDIR_UP = {0, 0, 1}; +static vec3_t VEC_DOWN = {0, -2, 0}; +static vec3_t MOVEDIR_DOWN = {0, 0, -1}; + +void AAS_SetMovedir(vec3_t angles, vec3_t movedir) +{ + if (VectorCompare(angles, VEC_UP)) + { + VectorCopy(MOVEDIR_UP, movedir); + } //end if + else if (VectorCompare(angles, VEC_DOWN)) + { + VectorCopy(MOVEDIR_DOWN, movedir); + } //end else if + else + { + AngleVectors(angles, movedir, NULL, NULL); + } //end else +} //end of the function AAS_SetMovedir +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_JumpReachRunStart(aas_reachability_t *reach, vec3_t runstart) +{ + vec3_t hordir, start, cmdmove; + aas_clientmove_t move; + + // + hordir[0] = reach->start[0] - reach->end[0]; + hordir[1] = reach->start[1] - reach->end[1]; + hordir[2] = 0; + VectorNormalize(hordir); + //start point + VectorCopy(reach->start, start); + start[2] += 1; + //get command movement + VectorScale(hordir, 400, cmdmove); + // + AAS_PredictClientMovement(&move, -1, start, PRESENCE_NORMAL, qtrue, + vec3_origin, cmdmove, 1, 2, 0.1f, + SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA| + SE_HITGROUNDDAMAGE|SE_GAP, 0, qfalse); + VectorCopy(move.endpos, runstart); + //don't enter slime or lava and don't fall from too high + if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) + { + VectorCopy(start, runstart); + } //end if +} //end of the function AAS_JumpReachRunStart +//=========================================================================== +// returns the Z velocity when rocket jumping at the origin +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_WeaponJumpZVelocity(vec3_t origin, float radiusdamage) +{ + vec3_t kvel, v, start, end, forward, right, viewangles, dir; + float mass, knockback, points; + vec3_t rocketoffset = {8, 8, -8}; + vec3_t botmins = {-16, -16, -24}; + vec3_t botmaxs = {16, 16, 32}; + bsp_trace_t bsptrace; + + //look down (90 degrees) + viewangles[PITCH] = 90; + viewangles[YAW] = 0; + viewangles[ROLL] = 0; + //get the start point shooting from + VectorCopy(origin, start); + start[2] += 8; //view offset Z + AngleVectors(viewangles, forward, right, NULL); + start[0] += forward[0] * rocketoffset[0] + right[0] * rocketoffset[1]; + start[1] += forward[1] * rocketoffset[0] + right[1] * rocketoffset[1]; + start[2] += forward[2] * rocketoffset[0] + right[2] * rocketoffset[1] + rocketoffset[2]; + //end point of the trace + VectorMA(start, 500, forward, end); + //trace a line to get the impact point + bsptrace = AAS_Trace(start, NULL, NULL, end, 1, CONTENTS_SOLID); + //calculate the damage the bot will get from the rocket impact + VectorAdd(botmins, botmaxs, v); + VectorMA(origin, 0.5, v, v); + VectorSubtract(bsptrace.endpos, v, v); + // + points = radiusdamage - 0.5 * VectorLength(v); + if (points < 0) points = 0; + //the owner of the rocket gets half the damage + points *= 0.5; + //mass of the bot (p_client.c: PutClientInServer) + mass = 200; + //knockback is the same as the damage points + knockback = points; + //direction of the damage (from trace.endpos to bot origin) + VectorSubtract(origin, bsptrace.endpos, dir); + VectorNormalize(dir); + //damage velocity + VectorScale(dir, 1600.0 * (float)knockback / mass, kvel); //the rocket jump hack... + //rocket impact velocity + jump velocity + return kvel[2] + aassettings.phys_jumpvel; +} //end of the function AAS_WeaponJumpZVelocity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_RocketJumpZVelocity(vec3_t origin) +{ + //rocket radius damage is 120 (p_weapon.c: Weapon_RocketLauncher_Fire) + return AAS_WeaponJumpZVelocity(origin, 120); +} //end of the function AAS_RocketJumpZVelocity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_BFGJumpZVelocity(vec3_t origin) +{ + //bfg radius damage is 1000 (p_weapon.c: weapon_bfg_fire) + return AAS_WeaponJumpZVelocity(origin, 120); +} //end of the function AAS_BFGJumpZVelocity +//=========================================================================== +// applies ground friction to the given velocity +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Accelerate(vec3_t velocity, float frametime, vec3_t wishdir, float wishspeed, float accel) +{ + // q2 style + int i; + float addspeed, accelspeed, currentspeed; + + currentspeed = DotProduct(velocity, wishdir); + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) { + return; + } + accelspeed = accel*frametime*wishspeed; + if (accelspeed > addspeed) { + accelspeed = addspeed; + } + + for (i=0 ; i<3 ; i++) { + velocity[i] += accelspeed*wishdir[i]; + } +} //end of the function AAS_Accelerate +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_AirControl(vec3_t start, vec3_t end, vec3_t velocity, vec3_t cmdmove) +{ + vec3_t dir; + + VectorSubtract(end, start, dir); +} //end of the function AAS_AirControl +//=========================================================================== +// applies ground friction to the given velocity +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ApplyFriction(vec3_t vel, float friction, float stopspeed, + float frametime) +{ + float speed, control, newspeed; + + //horizontal speed + speed = sqrt(vel[0] * vel[0] + vel[1] * vel[1]); + if (speed) + { + control = speed < stopspeed ? stopspeed : speed; + newspeed = speed - frametime * control * friction; + if (newspeed < 0) newspeed = 0; + newspeed /= speed; + vel[0] *= newspeed; + vel[1] *= newspeed; + } //end if +} //end of the function AAS_ApplyFriction +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_ClipToBBox(aas_trace_t *trace, vec3_t start, vec3_t end, int presencetype, vec3_t mins, vec3_t maxs) +{ + int i, j, side; + float front, back, frac, planedist; + vec3_t bboxmins, bboxmaxs, absmins, absmaxs, dir, mid; + + AAS_PresenceTypeBoundingBox(presencetype, bboxmins, bboxmaxs); + VectorSubtract(mins, bboxmaxs, absmins); + VectorSubtract(maxs, bboxmins, absmaxs); + // + VectorCopy(end, trace->endpos); + trace->fraction = 1; + for (i = 0; i < 3; i++) + { + if (start[i] < absmins[i] && end[i] < absmins[i]) return qfalse; + if (start[i] > absmaxs[i] && end[i] > absmaxs[i]) return qfalse; + } //end for + //check bounding box collision + VectorSubtract(end, start, dir); + frac = 1; + for (i = 0; i < 3; i++) + { + //get plane to test collision with for the current axis direction + if (dir[i] > 0) planedist = absmins[i]; + else planedist = absmaxs[i]; + //calculate collision fraction + front = start[i] - planedist; + back = end[i] - planedist; + frac = front / (front-back); + //check if between bounding planes of next axis + side = i + 1; + if (side > 2) side = 0; + mid[side] = start[side] + dir[side] * frac; + if (mid[side] > absmins[side] && mid[side] < absmaxs[side]) + { + //check if between bounding planes of next axis + side++; + if (side > 2) side = 0; + mid[side] = start[side] + dir[side] * frac; + if (mid[side] > absmins[side] && mid[side] < absmaxs[side]) + { + mid[i] = planedist; + break; + } //end if + } //end if + } //end for + //if there was a collision + if (i != 3) + { + trace->startsolid = qfalse; + trace->fraction = frac; + trace->ent = 0; + trace->planenum = 0; + trace->area = 0; + trace->lastarea = 0; + //trace endpos + for (j = 0; j < 3; j++) trace->endpos[j] = start[j] + dir[j] * frac; + return qtrue; + } //end if + return qfalse; +} //end of the function AAS_ClipToBBox +//=========================================================================== +// predicts the movement +// assumes regular bounding box sizes +// NOTE: out of water jumping is not included +// NOTE: grappling hook is not included +// +// Parameter: origin : origin to start with +// presencetype : presence type to start with +// velocity : velocity to start with +// cmdmove : client command movement +// cmdframes : number of frame cmdmove is valid +// maxframes : maximum number of predicted frames +// frametime : duration of one predicted frame +// stopevent : events that stop the prediction +// stopareanum : stop as soon as entered this area +// Returns: aas_clientmove_t +// Changes Globals: - +//=========================================================================== +int AAS_ClientMovementPrediction(struct aas_clientmove_s *move, + int entnum, vec3_t origin, + int presencetype, int onground, + vec3_t velocity, vec3_t cmdmove, + int cmdframes, + int maxframes, float frametime, + int stopevent, int stopareanum, + vec3_t mins, vec3_t maxs, int visualize) +{ + float phys_friction, phys_stopspeed, phys_gravity, phys_waterfriction; + float phys_watergravity; + float phys_walkaccelerate, phys_airaccelerate, phys_swimaccelerate; + float phys_maxwalkvelocity, phys_maxcrouchvelocity, phys_maxswimvelocity; + float phys_maxstep, phys_maxsteepness, phys_jumpvel, friction; + float gravity, delta, maxvel, wishspeed, accelerate; + //float velchange, newvel; + int n, i, j, pc, step, swimming, ax, crouch, event, jump_frame, areanum; + int areas[20], numareas; + vec3_t points[20]; + vec3_t org, end, feet, start, stepend, lastorg, wishdir; + vec3_t frame_test_vel, old_frame_test_vel, left_test_vel; + vec3_t up = {0, 0, 1}; + aas_plane_t *plane, *plane2; + aas_trace_t trace, steptrace; + + if (frametime <= 0) frametime = 0.1f; + // + phys_friction = aassettings.phys_friction; + phys_stopspeed = aassettings.phys_stopspeed; + phys_gravity = aassettings.phys_gravity; + phys_waterfriction = aassettings.phys_waterfriction; + phys_watergravity = aassettings.phys_watergravity; + phys_maxwalkvelocity = aassettings.phys_maxwalkvelocity;// * frametime; + phys_maxcrouchvelocity = aassettings.phys_maxcrouchvelocity;// * frametime; + phys_maxswimvelocity = aassettings.phys_maxswimvelocity;// * frametime; + phys_walkaccelerate = aassettings.phys_walkaccelerate; + phys_airaccelerate = aassettings.phys_airaccelerate; + phys_swimaccelerate = aassettings.phys_swimaccelerate; + phys_maxstep = aassettings.phys_maxstep; + phys_maxsteepness = aassettings.phys_maxsteepness; + phys_jumpvel = aassettings.phys_jumpvel * frametime; + // + Com_Memset(move, 0, sizeof(aas_clientmove_t)); + Com_Memset(&trace, 0, sizeof(aas_trace_t)); + //start at the current origin + VectorCopy(origin, org); + org[2] += 0.25; + //velocity to test for the first frame + VectorScale(velocity, frametime, frame_test_vel); + // + jump_frame = -1; + //predict a maximum of 'maxframes' ahead + for (n = 0; n < maxframes; n++) + { + swimming = AAS_Swimming(org); + //get gravity depending on swimming or not + gravity = swimming ? phys_watergravity : phys_gravity; + //apply gravity at the START of the frame + frame_test_vel[2] = frame_test_vel[2] - (gravity * 0.1 * frametime); + //if on the ground or swimming + if (onground || swimming) + { + friction = swimming ? phys_friction : phys_waterfriction; + //apply friction + VectorScale(frame_test_vel, 1/frametime, frame_test_vel); + AAS_ApplyFriction(frame_test_vel, friction, phys_stopspeed, frametime); + VectorScale(frame_test_vel, frametime, frame_test_vel); + } //end if + crouch = qfalse; + //apply command movement + if (n < cmdframes) + { + ax = 0; + maxvel = phys_maxwalkvelocity; + accelerate = phys_airaccelerate; + VectorCopy(cmdmove, wishdir); + if (onground) + { + if (cmdmove[2] < -300) + { + crouch = qtrue; + maxvel = phys_maxcrouchvelocity; + } //end if + //if not swimming and upmove is positive then jump + if (!swimming && cmdmove[2] > 1) + { + //jump velocity minus the gravity for one frame + 5 for safety + frame_test_vel[2] = phys_jumpvel - (gravity * 0.1 * frametime) + 5; + jump_frame = n; + //jumping so air accelerate + accelerate = phys_airaccelerate; + } //end if + else + { + accelerate = phys_walkaccelerate; + } //end else + ax = 2; + } //end if + if (swimming) + { + maxvel = phys_maxswimvelocity; + accelerate = phys_swimaccelerate; + ax = 3; + } //end if + else + { + wishdir[2] = 0; + } //end else + // + wishspeed = VectorNormalize(wishdir); + if (wishspeed > maxvel) wishspeed = maxvel; + VectorScale(frame_test_vel, 1/frametime, frame_test_vel); + AAS_Accelerate(frame_test_vel, frametime, wishdir, wishspeed, accelerate); + VectorScale(frame_test_vel, frametime, frame_test_vel); + /* + for (i = 0; i < ax; i++) + { + velchange = (cmdmove[i] * frametime) - frame_test_vel[i]; + if (velchange > phys_maxacceleration) velchange = phys_maxacceleration; + else if (velchange < -phys_maxacceleration) velchange = -phys_maxacceleration; + newvel = frame_test_vel[i] + velchange; + // + if (frame_test_vel[i] <= maxvel && newvel > maxvel) frame_test_vel[i] = maxvel; + else if (frame_test_vel[i] >= -maxvel && newvel < -maxvel) frame_test_vel[i] = -maxvel; + else frame_test_vel[i] = newvel; + } //end for + */ + } //end if + if (crouch) + { + presencetype = PRESENCE_CROUCH; + } //end if + else if (presencetype == PRESENCE_CROUCH) + { + if (AAS_PointPresenceType(org) & PRESENCE_NORMAL) + { + presencetype = PRESENCE_NORMAL; + } //end if + } //end else + //save the current origin + VectorCopy(org, lastorg); + //move linear during one frame + VectorCopy(frame_test_vel, left_test_vel); + j = 0; + do + { + VectorAdd(org, left_test_vel, end); + //trace a bounding box + trace = AAS_TraceClientBBox(org, end, presencetype, entnum); + // +//#ifdef AAS_MOVE_DEBUG + if (visualize) + { + if (trace.startsolid) botimport.Print(PRT_MESSAGE, "PredictMovement: start solid\n"); + AAS_DebugLine(org, trace.endpos, LINECOLOR_RED); + } //end if +//#endif //AAS_MOVE_DEBUG + // + if (stopevent & (SE_ENTERAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_TOUCHCLUSTERPORTAL)) + { + numareas = AAS_TraceAreas(org, trace.endpos, areas, points, 20); + for (i = 0; i < numareas; i++) + { + if (stopevent & SE_ENTERAREA) + { + if (areas[i] == stopareanum) + { + VectorCopy(points[i], move->endpos); + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->endarea = areas[i]; + move->trace = trace; + move->stopevent = SE_ENTERAREA; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + //NOTE: if not the first frame + if ((stopevent & SE_TOUCHJUMPPAD) && n) + { + if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_JUMPPAD) + { + VectorCopy(points[i], move->endpos); + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->endarea = areas[i]; + move->trace = trace; + move->stopevent = SE_TOUCHJUMPPAD; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + if (stopevent & SE_TOUCHTELEPORTER) + { + if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_TELEPORTER) + { + VectorCopy(points[i], move->endpos); + move->endarea = areas[i]; + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_TOUCHTELEPORTER; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + if (stopevent & SE_TOUCHCLUSTERPORTAL) + { + if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_CLUSTERPORTAL) + { + VectorCopy(points[i], move->endpos); + move->endarea = areas[i]; + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_TOUCHCLUSTERPORTAL; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + } //end for + } //end if + // + if (stopevent & SE_HITBOUNDINGBOX) + { + if (AAS_ClipToBBox(&trace, org, trace.endpos, presencetype, mins, maxs)) + { + VectorCopy(trace.endpos, move->endpos); + move->endarea = AAS_PointAreaNum(move->endpos); + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_HITBOUNDINGBOX; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + //move the entity to the trace end point + VectorCopy(trace.endpos, org); + //if there was a collision + if (trace.fraction < 1.0) + { + //get the plane the bounding box collided with + plane = AAS_PlaneFromNum(trace.planenum); + // + if (stopevent & SE_HITGROUNDAREA) + { + if (DotProduct(plane->normal, up) > phys_maxsteepness) + { + VectorCopy(org, start); + start[2] += 0.5; + if (AAS_PointAreaNum(start) == stopareanum) + { + VectorCopy(start, move->endpos); + move->endarea = stopareanum; + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_HITGROUNDAREA; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + } //end if + //assume there's no step + step = qfalse; + //if it is a vertical plane and the bot didn't jump recently + if (plane->normal[2] == 0 && (jump_frame < 0 || n - jump_frame > 2)) + { + //check for a step + VectorMA(org, -0.25, plane->normal, start); + VectorCopy(start, stepend); + start[2] += phys_maxstep; + steptrace = AAS_TraceClientBBox(start, stepend, presencetype, entnum); + // + if (!steptrace.startsolid) + { + plane2 = AAS_PlaneFromNum(steptrace.planenum); + if (DotProduct(plane2->normal, up) > phys_maxsteepness) + { + VectorSubtract(end, steptrace.endpos, left_test_vel); + left_test_vel[2] = 0; + frame_test_vel[2] = 0; +//#ifdef AAS_MOVE_DEBUG + if (visualize) + { + if (steptrace.endpos[2] - org[2] > 0.125) + { + VectorCopy(org, start); + start[2] = steptrace.endpos[2]; + AAS_DebugLine(org, start, LINECOLOR_BLUE); + } //end if + } //end if +//#endif //AAS_MOVE_DEBUG + org[2] = steptrace.endpos[2]; + step = qtrue; + } //end if + } //end if + } //end if + // + if (!step) + { + //velocity left to test for this frame is the projection + //of the current test velocity into the hit plane + VectorMA(left_test_vel, -DotProduct(left_test_vel, plane->normal), + plane->normal, left_test_vel); + //store the old velocity for landing check + VectorCopy(frame_test_vel, old_frame_test_vel); + //test velocity for the next frame is the projection + //of the velocity of the current frame into the hit plane + VectorMA(frame_test_vel, -DotProduct(frame_test_vel, plane->normal), + plane->normal, frame_test_vel); + //check for a landing on an almost horizontal floor + if (DotProduct(plane->normal, up) > phys_maxsteepness) + { + onground = qtrue; + } //end if + if (stopevent & SE_HITGROUNDDAMAGE) + { + delta = 0; + if (old_frame_test_vel[2] < 0 && + frame_test_vel[2] > old_frame_test_vel[2] && + !onground) + { + delta = old_frame_test_vel[2]; + } //end if + else if (onground) + { + delta = frame_test_vel[2] - old_frame_test_vel[2]; + } //end else + if (delta) + { + delta = delta * 10; + delta = delta * delta * 0.0001; + if (swimming) delta = 0; + // never take falling damage if completely underwater + /* + if (ent->waterlevel == 3) return; + if (ent->waterlevel == 2) delta *= 0.25; + if (ent->waterlevel == 1) delta *= 0.5; + */ + if (delta > 40) + { + VectorCopy(org, move->endpos); + move->endarea = AAS_PointAreaNum(org); + VectorCopy(frame_test_vel, move->velocity); + move->trace = trace; + move->stopevent = SE_HITGROUNDDAMAGE; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + } //end if + } //end if + } //end if + //extra check to prevent endless loop + if (++j > 20) return qfalse; + //while there is a plane hit + } while(trace.fraction < 1.0); + //if going down + if (frame_test_vel[2] <= 10) + { + //check for a liquid at the feet of the bot + VectorCopy(org, feet); + feet[2] -= 22; + pc = AAS_PointContents(feet); + //get event from pc + event = SE_NONE; + if (pc & CONTENTS_LAVA) event |= SE_ENTERLAVA; + if (pc & CONTENTS_SLIME) event |= SE_ENTERSLIME; + if (pc & CONTENTS_WATER) event |= SE_ENTERWATER; + // + areanum = AAS_PointAreaNum(org); + if (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA) + event |= SE_ENTERLAVA; + if (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME) + event |= SE_ENTERSLIME; + if (aasworld.areasettings[areanum].contents & AREACONTENTS_WATER) + event |= SE_ENTERWATER; + //if in lava or slime + if (event & stopevent) + { + VectorCopy(org, move->endpos); + move->endarea = areanum; + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->stopevent = event & stopevent; + move->presencetype = presencetype; + move->endcontents = pc; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + // + onground = AAS_OnGround(org, presencetype, entnum); + //if onground and on the ground for at least one whole frame + if (onground) + { + if (stopevent & SE_HITGROUND) + { + VectorCopy(org, move->endpos); + move->endarea = AAS_PointAreaNum(org); + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_HITGROUND; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + else if (stopevent & SE_LEAVEGROUND) + { + VectorCopy(org, move->endpos); + move->endarea = AAS_PointAreaNum(org); + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_LEAVEGROUND; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end else if + else if (stopevent & SE_GAP) + { + aas_trace_t gaptrace; + + VectorCopy(org, start); + VectorCopy(start, end); + end[2] -= 48 + aassettings.phys_maxbarrier; + gaptrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); + //if solid is found the bot cannot walk any further and will not fall into a gap + if (!gaptrace.startsolid) + { + //if it is a gap (lower than one step height) + if (gaptrace.endpos[2] < org[2] - aassettings.phys_maxstep - 1) + { + if (!(AAS_PointContents(end) & CONTENTS_WATER)) + { + VectorCopy(lastorg, move->endpos); + move->endarea = AAS_PointAreaNum(lastorg); + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_GAP; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + } //end if + } //end else if + } //end for + // + VectorCopy(org, move->endpos); + move->endarea = AAS_PointAreaNum(org); + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->stopevent = SE_NONE; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + // + return qtrue; +} //end of the function AAS_ClientMovementPrediction +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PredictClientMovement(struct aas_clientmove_s *move, + int entnum, vec3_t origin, + int presencetype, int onground, + vec3_t velocity, vec3_t cmdmove, + int cmdframes, + int maxframes, float frametime, + int stopevent, int stopareanum, int visualize) +{ + vec3_t mins, maxs; + return AAS_ClientMovementPrediction(move, entnum, origin, presencetype, onground, + velocity, cmdmove, cmdframes, maxframes, + frametime, stopevent, stopareanum, + mins, maxs, visualize); +} //end of the function AAS_PredictClientMovement +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_ClientMovementHitBBox(struct aas_clientmove_s *move, + int entnum, vec3_t origin, + int presencetype, int onground, + vec3_t velocity, vec3_t cmdmove, + int cmdframes, + int maxframes, float frametime, + vec3_t mins, vec3_t maxs, int visualize) +{ + return AAS_ClientMovementPrediction(move, entnum, origin, presencetype, onground, + velocity, cmdmove, cmdframes, maxframes, + frametime, SE_HITBOUNDINGBOX, 0, + mins, maxs, visualize); +} //end of the function AAS_ClientMovementHitBBox +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_TestMovementPrediction(int entnum, vec3_t origin, vec3_t dir) +{ + vec3_t velocity, cmdmove; + aas_clientmove_t move; + + VectorClear(velocity); + if (!AAS_Swimming(origin)) dir[2] = 0; + VectorNormalize(dir); + VectorScale(dir, 400, cmdmove); + cmdmove[2] = 224; + AAS_ClearShownDebugLines(); + AAS_PredictClientMovement(&move, entnum, origin, PRESENCE_NORMAL, qtrue, + velocity, cmdmove, 13, 13, 0.1f, SE_HITGROUND, 0, qtrue);//SE_LEAVEGROUND); + if (move.stopevent & SE_LEAVEGROUND) + { + botimport.Print(PRT_MESSAGE, "leave ground\n"); + } //end if +} //end of the function TestMovementPrediction +//=========================================================================== +// calculates the horizontal velocity needed to perform a jump from start +// to end +// +// Parameter: zvel : z velocity for jump +// start : start position of jump +// end : end position of jump +// *speed : returned speed for jump +// Returns: qfalse if too high or too far from start to end +// Changes Globals: - +//=========================================================================== +int AAS_HorizontalVelocityForJump(float zvel, vec3_t start, vec3_t end, float *velocity) +{ + float phys_gravity, phys_maxvelocity; + float maxjump, height2fall, t, top; + vec3_t dir; + + phys_gravity = aassettings.phys_gravity; + phys_maxvelocity = aassettings.phys_maxvelocity; + + //maximum height a player can jump with the given initial z velocity + maxjump = 0.5 * phys_gravity * (zvel / phys_gravity) * (zvel / phys_gravity); + //top of the parabolic jump + top = start[2] + maxjump; + //height the bot will fall from the top + height2fall = top - end[2]; + //if the goal is to high to jump to + if (height2fall < 0) + { + *velocity = phys_maxvelocity; + return 0; + } //end if + //time a player takes to fall the height + t = sqrt(height2fall / (0.5 * phys_gravity)); + //direction from start to end + VectorSubtract(end, start, dir); + // + if ( (t + zvel / phys_gravity) == 0.0f ) { + *velocity = phys_maxvelocity; + return 0; + } + //calculate horizontal speed + *velocity = sqrt(dir[0]*dir[0] + dir[1]*dir[1]) / (t + zvel / phys_gravity); + //the horizontal speed must be lower than the max speed + if (*velocity > phys_maxvelocity) + { + *velocity = phys_maxvelocity; + return 0; + } //end if + return 1; +} //end of the function AAS_HorizontalVelocityForJump diff --git a/reaction/engine/code/botlib/be_aas_move.h b/reaction/engine/code/botlib/be_aas_move.h new file mode 100644 index 00000000..b00e41ab --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_move.h @@ -0,0 +1,71 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_move.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_move.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +extern aas_settings_t aassettings; +#endif //AASINTERN + +//movement prediction +int AAS_PredictClientMovement(struct aas_clientmove_s *move, + int entnum, vec3_t origin, + int presencetype, int onground, + vec3_t velocity, vec3_t cmdmove, + int cmdframes, + int maxframes, float frametime, + int stopevent, int stopareanum, int visualize); +//predict movement until bounding box is hit +int AAS_ClientMovementHitBBox(struct aas_clientmove_s *move, + int entnum, vec3_t origin, + int presencetype, int onground, + vec3_t velocity, vec3_t cmdmove, + int cmdframes, + int maxframes, float frametime, + vec3_t mins, vec3_t maxs, int visualize); +//returns true if on the ground at the given origin +int AAS_OnGround(vec3_t origin, int presencetype, int passent); +//returns true if swimming at the given origin +int AAS_Swimming(vec3_t origin); +//returns the jump reachability run start point +void AAS_JumpReachRunStart(struct aas_reachability_s *reach, vec3_t runstart); +//returns true if against a ladder at the given origin +int AAS_AgainstLadder(vec3_t origin); +//rocket jump Z velocity when rocket-jumping at origin +float AAS_RocketJumpZVelocity(vec3_t origin); +//bfg jump Z velocity when bfg-jumping at origin +float AAS_BFGJumpZVelocity(vec3_t origin); +//calculates the horizontal velocity needed for a jump and returns true this velocity could be calculated +int AAS_HorizontalVelocityForJump(float zvel, vec3_t start, vec3_t end, float *velocity); +// +void AAS_SetMovedir(vec3_t angles, vec3_t movedir); +// +int AAS_DropToFloor(vec3_t origin, vec3_t mins, vec3_t maxs); +// +void AAS_InitSettings(void); diff --git a/reaction/engine/code/botlib/be_aas_optimize.c b/reaction/engine/code/botlib/be_aas_optimize.c new file mode 100644 index 00000000..ea0d2da6 --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_optimize.c @@ -0,0 +1,312 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_optimize.c + * + * desc: decreases the .aas file size after the reachabilities have + * been calculated, just dumps all the faces, edges and vertexes + * + * $Archive: /MissionPack/code/botlib/be_aas_optimize.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_libvar.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_aas_def.h" + +typedef struct optimized_s +{ + //vertexes + int numvertexes; + aas_vertex_t *vertexes; + //edges + int numedges; + aas_edge_t *edges; + //edge index + int edgeindexsize; + aas_edgeindex_t *edgeindex; + //faces + int numfaces; + aas_face_t *faces; + //face index + int faceindexsize; + aas_faceindex_t *faceindex; + //convex areas + int numareas; + aas_area_t *areas; + // + int *vertexoptimizeindex; + int *edgeoptimizeindex; + int *faceoptimizeindex; +} optimized_t; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_KeepEdge(aas_edge_t *edge) +{ + return 1; +} //end of the function AAS_KeepFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_OptimizeEdge(optimized_t *optimized, int edgenum) +{ + int i, optedgenum; + aas_edge_t *edge, *optedge; + + edge = &aasworld.edges[abs(edgenum)]; + if (!AAS_KeepEdge(edge)) return 0; + + optedgenum = optimized->edgeoptimizeindex[abs(edgenum)]; + if (optedgenum) + { + //keep the edge reversed sign + if (edgenum > 0) return optedgenum; + else return -optedgenum; + } //end if + + optedge = &optimized->edges[optimized->numedges]; + + for (i = 0; i < 2; i++) + { + if (optimized->vertexoptimizeindex[edge->v[i]]) + { + optedge->v[i] = optimized->vertexoptimizeindex[edge->v[i]]; + } //end if + else + { + VectorCopy(aasworld.vertexes[edge->v[i]], optimized->vertexes[optimized->numvertexes]); + optedge->v[i] = optimized->numvertexes; + optimized->vertexoptimizeindex[edge->v[i]] = optimized->numvertexes; + optimized->numvertexes++; + } //end else + } //end for + optimized->edgeoptimizeindex[abs(edgenum)] = optimized->numedges; + optedgenum = optimized->numedges; + optimized->numedges++; + //keep the edge reversed sign + if (edgenum > 0) return optedgenum; + else return -optedgenum; +} //end of the function AAS_OptimizeEdge +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_KeepFace(aas_face_t *face) +{ + if (!(face->faceflags & FACE_LADDER)) return 0; + else return 1; +} //end of the function AAS_KeepFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_OptimizeFace(optimized_t *optimized, int facenum) +{ + int i, edgenum, optedgenum, optfacenum; + aas_face_t *face, *optface; + + face = &aasworld.faces[abs(facenum)]; + if (!AAS_KeepFace(face)) return 0; + + optfacenum = optimized->faceoptimizeindex[abs(facenum)]; + if (optfacenum) + { + //keep the face side sign + if (facenum > 0) return optfacenum; + else return -optfacenum; + } //end if + + optface = &optimized->faces[optimized->numfaces]; + Com_Memcpy(optface, face, sizeof(aas_face_t)); + + optface->numedges = 0; + optface->firstedge = optimized->edgeindexsize; + for (i = 0; i < face->numedges; i++) + { + edgenum = aasworld.edgeindex[face->firstedge + i]; + optedgenum = AAS_OptimizeEdge(optimized, edgenum); + if (optedgenum) + { + optimized->edgeindex[optface->firstedge + optface->numedges] = optedgenum; + optface->numedges++; + optimized->edgeindexsize++; + } //end if + } //end for + optimized->faceoptimizeindex[abs(facenum)] = optimized->numfaces; + optfacenum = optimized->numfaces; + optimized->numfaces++; + //keep the face side sign + if (facenum > 0) return optfacenum; + else return -optfacenum; +} //end of the function AAS_OptimizeFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_OptimizeArea(optimized_t *optimized, int areanum) +{ + int i, facenum, optfacenum; + aas_area_t *area, *optarea; + + area = &aasworld.areas[areanum]; + optarea = &optimized->areas[areanum]; + Com_Memcpy(optarea, area, sizeof(aas_area_t)); + + optarea->numfaces = 0; + optarea->firstface = optimized->faceindexsize; + for (i = 0; i < area->numfaces; i++) + { + facenum = aasworld.faceindex[area->firstface + i]; + optfacenum = AAS_OptimizeFace(optimized, facenum); + if (optfacenum) + { + optimized->faceindex[optarea->firstface + optarea->numfaces] = optfacenum; + optarea->numfaces++; + optimized->faceindexsize++; + } //end if + } //end for +} //end of the function AAS_OptimizeArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_OptimizeAlloc(optimized_t *optimized) +{ + optimized->vertexes = (aas_vertex_t *) GetClearedMemory(aasworld.numvertexes * sizeof(aas_vertex_t)); + optimized->numvertexes = 0; + optimized->edges = (aas_edge_t *) GetClearedMemory(aasworld.numedges * sizeof(aas_edge_t)); + optimized->numedges = 1; //edge zero is a dummy + optimized->edgeindex = (aas_edgeindex_t *) GetClearedMemory(aasworld.edgeindexsize * sizeof(aas_edgeindex_t)); + optimized->edgeindexsize = 0; + optimized->faces = (aas_face_t *) GetClearedMemory(aasworld.numfaces * sizeof(aas_face_t)); + optimized->numfaces = 1; //face zero is a dummy + optimized->faceindex = (aas_faceindex_t *) GetClearedMemory(aasworld.faceindexsize * sizeof(aas_faceindex_t)); + optimized->faceindexsize = 0; + optimized->areas = (aas_area_t *) GetClearedMemory(aasworld.numareas * sizeof(aas_area_t)); + optimized->numareas = aasworld.numareas; + // + optimized->vertexoptimizeindex = (int *) GetClearedMemory(aasworld.numvertexes * sizeof(int)); + optimized->edgeoptimizeindex = (int *) GetClearedMemory(aasworld.numedges * sizeof(int)); + optimized->faceoptimizeindex = (int *) GetClearedMemory(aasworld.numfaces * sizeof(int)); +} //end of the function AAS_OptimizeAlloc +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_OptimizeStore(optimized_t *optimized) +{ + //store the optimized vertexes + if (aasworld.vertexes) FreeMemory(aasworld.vertexes); + aasworld.vertexes = optimized->vertexes; + aasworld.numvertexes = optimized->numvertexes; + //store the optimized edges + if (aasworld.edges) FreeMemory(aasworld.edges); + aasworld.edges = optimized->edges; + aasworld.numedges = optimized->numedges; + //store the optimized edge index + if (aasworld.edgeindex) FreeMemory(aasworld.edgeindex); + aasworld.edgeindex = optimized->edgeindex; + aasworld.edgeindexsize = optimized->edgeindexsize; + //store the optimized faces + if (aasworld.faces) FreeMemory(aasworld.faces); + aasworld.faces = optimized->faces; + aasworld.numfaces = optimized->numfaces; + //store the optimized face index + if (aasworld.faceindex) FreeMemory(aasworld.faceindex); + aasworld.faceindex = optimized->faceindex; + aasworld.faceindexsize = optimized->faceindexsize; + //store the optimized areas + if (aasworld.areas) FreeMemory(aasworld.areas); + aasworld.areas = optimized->areas; + aasworld.numareas = optimized->numareas; + //free optimize indexes + FreeMemory(optimized->vertexoptimizeindex); + FreeMemory(optimized->edgeoptimizeindex); + FreeMemory(optimized->faceoptimizeindex); +} //end of the function AAS_OptimizeStore +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Optimize(void) +{ + int i, sign; + optimized_t optimized; + + AAS_OptimizeAlloc(&optimized); + for (i = 1; i < aasworld.numareas; i++) + { + AAS_OptimizeArea(&optimized, i); + } //end for + //reset the reachability face pointers + for (i = 0; i < aasworld.reachabilitysize; i++) + { + //NOTE: for TRAVEL_ELEVATOR the facenum is the model number of + // the elevator + if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) continue; + //NOTE: for TRAVEL_JUMPPAD the facenum is the Z velocity and the edgenum is the hor velocity + if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) continue; + //NOTE: for TRAVEL_FUNCBOB the facenum and edgenum contain other coded information + if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB) continue; + // + sign = aasworld.reachability[i].facenum; + aasworld.reachability[i].facenum = optimized.faceoptimizeindex[abs(aasworld.reachability[i].facenum)]; + if (sign < 0) aasworld.reachability[i].facenum = -aasworld.reachability[i].facenum; + sign = aasworld.reachability[i].edgenum; + aasworld.reachability[i].edgenum = optimized.edgeoptimizeindex[abs(aasworld.reachability[i].edgenum)]; + if (sign < 0) aasworld.reachability[i].edgenum = -aasworld.reachability[i].edgenum; + } //end for + //store the optimized AAS data into aasworld + AAS_OptimizeStore(&optimized); + //print some nice stuff :) + botimport.Print(PRT_MESSAGE, "AAS data optimized.\n"); +} //end of the function AAS_Optimize diff --git a/reaction/engine/code/botlib/be_aas_optimize.h b/reaction/engine/code/botlib/be_aas_optimize.h new file mode 100644 index 00000000..799c28a3 --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_optimize.h @@ -0,0 +1,33 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_optimize.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_optimize.h $ + * + *****************************************************************************/ + +void AAS_Optimize(void); + diff --git a/reaction/engine/code/botlib/be_aas_reach.c b/reaction/engine/code/botlib/be_aas_reach.c new file mode 100644 index 00000000..95ead20c --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_reach.c @@ -0,0 +1,4538 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_reach.c + * + * desc: reachability calculations + * + * $Archive: /MissionPack/code/botlib/be_aas_reach.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_log.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_libvar.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_aas_def.h" + +extern int Sys_MilliSeconds(void); + + +extern botlib_import_t botimport; + +//#define REACH_DEBUG + +//NOTE: all travel times are in hundreth of a second +//maximum number of reachability links +#define AAS_MAX_REACHABILITYSIZE 65536 +//number of areas reachability is calculated for each frame +#define REACHABILITYAREASPERCYCLE 15 +//number of units reachability points are placed inside the areas +#define INSIDEUNITS 2 +#define INSIDEUNITS_WALKEND 5 +#define INSIDEUNITS_WALKSTART 0.1 +#define INSIDEUNITS_WATERJUMP 15 +//area flag used for weapon jumping +#define AREA_WEAPONJUMP 8192 //valid area to weapon jump to +//number of reachabilities of each type +int reach_swim; //swim +int reach_equalfloor; //walk on floors with equal height +int reach_step; //step up +int reach_walk; //walk of step +int reach_barrier; //jump up to a barrier +int reach_waterjump; //jump out of water +int reach_walkoffledge; //walk of a ledge +int reach_jump; //jump +int reach_ladder; //climb or descent a ladder +int reach_teleport; //teleport +int reach_elevator; //use an elevator +int reach_funcbob; //use a func bob +int reach_grapple; //grapple hook +int reach_doublejump; //double jump +int reach_rampjump; //ramp jump +int reach_strafejump; //strafe jump (just normal jump but further) +int reach_rocketjump; //rocket jump +int reach_bfgjump; //bfg jump +int reach_jumppad; //jump pads +//if true grapple reachabilities are skipped +int calcgrapplereach; +//linked reachability +typedef struct aas_lreachability_s +{ + int areanum; //number of the reachable area + int facenum; //number of the face towards the other area + int edgenum; //number of the edge towards the other area + vec3_t start; //start point of inter area movement + vec3_t end; //end point of inter area movement + int traveltype; //type of travel required to get to the area + unsigned short int traveltime; //travel time of the inter area movement + // + struct aas_lreachability_s *next; +} aas_lreachability_t; +//temporary reachabilities +aas_lreachability_t *reachabilityheap; //heap with reachabilities +aas_lreachability_t *nextreachability; //next free reachability from the heap +aas_lreachability_t **areareachability; //reachability links for every area +int numlreachabilities; + +//=========================================================================== +// returns the surface area of the given face +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_FaceArea(aas_face_t *face) +{ + int i, edgenum, side; + float total; + vec_t *v; + vec3_t d1, d2, cross; + aas_edge_t *edge; + + edgenum = aasworld.edgeindex[face->firstedge]; + side = edgenum < 0; + edge = &aasworld.edges[abs(edgenum)]; + v = aasworld.vertexes[edge->v[side]]; + + total = 0; + for (i = 1; i < face->numedges - 1; i++) + { + edgenum = aasworld.edgeindex[face->firstedge + i]; + side = edgenum < 0; + edge = &aasworld.edges[abs(edgenum)]; + VectorSubtract(aasworld.vertexes[edge->v[side]], v, d1); + VectorSubtract(aasworld.vertexes[edge->v[!side]], v, d2); + CrossProduct(d1, d2, cross); + total += 0.5 * VectorLength(cross); + } //end for + return total; +} //end of the function AAS_FaceArea +//=========================================================================== +// returns the volume of an area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_AreaVolume(int areanum) +{ + int i, edgenum, facenum, side; + vec_t d, a, volume; + vec3_t corner; + aas_plane_t *plane; + aas_edge_t *edge; + aas_face_t *face; + aas_area_t *area; + + area = &aasworld.areas[areanum]; + facenum = aasworld.faceindex[area->firstface]; + face = &aasworld.faces[abs(facenum)]; + edgenum = aasworld.edgeindex[face->firstedge]; + edge = &aasworld.edges[abs(edgenum)]; + // + VectorCopy(aasworld.vertexes[edge->v[0]], corner); + + //make tetrahedrons to all other faces + volume = 0; + for (i = 0; i < area->numfaces; i++) + { + facenum = abs(aasworld.faceindex[area->firstface + i]); + face = &aasworld.faces[facenum]; + side = face->backarea != areanum; + plane = &aasworld.planes[face->planenum ^ side]; + d = -(DotProduct (corner, plane->normal) - plane->dist); + a = AAS_FaceArea(face); + volume += d * a; + } //end for + + volume /= 3; + return volume; +} //end of the function AAS_AreaVolume +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BestReachableLinkArea(aas_link_t *areas) +{ + aas_link_t *link; + + for (link = areas; link; link = link->next_area) + { + if (AAS_AreaGrounded(link->areanum) || AAS_AreaSwim(link->areanum)) + { + return link->areanum; + } //end if + } //end for + // + for (link = areas; link; link = link->next_area) + { + if (link->areanum) return link->areanum; + //FIXME: this is a bad idea when the reachability is not yet + // calculated when the level items are loaded + if (AAS_AreaReachability(link->areanum)) + return link->areanum; + } //end for + return 0; +} //end of the function AAS_BestReachableLinkArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_GetJumpPadInfo(int ent, vec3_t areastart, vec3_t absmins, vec3_t absmaxs, vec3_t velocity) +{ + int modelnum, ent2; + float speed, height, gravity, time, dist, forward; + vec3_t origin, angles, teststart, ent2origin; + aas_trace_t trace; + char model[MAX_EPAIRKEY]; + char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY]; + + // + AAS_FloatForBSPEpairKey(ent, "speed", &speed); + if (!speed) speed = 1000; + VectorClear(angles); + //get the mins, maxs and origin of the model + AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); + if (model[0]) modelnum = atoi(model+1); + else modelnum = 0; + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin); + VectorAdd(origin, absmins, absmins); + VectorAdd(origin, absmaxs, absmaxs); + VectorAdd(absmins, absmaxs, origin); + VectorScale (origin, 0.5, origin); + + //get the start areas + VectorCopy(origin, teststart); + teststart[2] += 64; + trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1); + if (trace.startsolid) + { + botimport.Print(PRT_MESSAGE, "trigger_push start solid\n"); + VectorCopy(origin, areastart); + } //end if + else + { + VectorCopy(trace.endpos, areastart); + } //end else + areastart[2] += 0.125; + // + //AAS_DrawPermanentCross(origin, 4, 4); + //get the target entity + AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY); + for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2)) + { + if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue; + if (!strcmp(targetname, target)) break; + } //end for + if (!ent2) + { + botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target); + return qfalse; + } //end if + AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin); + // + height = ent2origin[2] - origin[2]; + gravity = aassettings.phys_gravity; + time = sqrt( height / ( 0.5 * gravity ) ); + if (!time) + { + botimport.Print(PRT_MESSAGE, "trigger_push without time\n"); + return qfalse; + } //end if + // set s.origin2 to the push velocity + VectorSubtract ( ent2origin, origin, velocity); + dist = VectorNormalize( velocity); + forward = dist / time; + //FIXME: why multiply by 1.1 + forward *= 1.1f; + VectorScale(velocity, forward, velocity); + velocity[2] = time * gravity; + return qtrue; +} //end of the function AAS_GetJumpPadInfo +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs) +{ + int area2num, ent, bot_visualizejumppads, bestareanum; + float volume, bestareavolume; + vec3_t areastart, cmdmove, bboxmins, bboxmaxs; + vec3_t absmins, absmaxs, velocity; + aas_clientmove_t move; + aas_link_t *areas, *link; + char classname[MAX_EPAIRKEY]; + +#ifdef BSPC + bot_visualizejumppads = 0; +#else + bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0"); +#endif + VectorAdd(origin, mins, bboxmins); + VectorAdd(origin, maxs, bboxmaxs); + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + if (strcmp(classname, "trigger_push")) continue; + // + if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue; + //get the areas the jump pad brush is in + areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); + for (link = areas; link; link = link->next_area) + { + if (AAS_AreaJumpPad(link->areanum)) break; + } //end for + if (!link) + { + botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n"); + AAS_UnlinkFromAreas(areas); + continue; + } //end if + // + //botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]); + // + VectorSet(cmdmove, 0, 0, 0); + Com_Memset(&move, 0, sizeof(aas_clientmove_t)); + area2num = 0; + AAS_ClientMovementHitBBox(&move, -1, areastart, PRESENCE_NORMAL, qfalse, + velocity, cmdmove, 0, 30, 0.1f, bboxmins, bboxmaxs, bot_visualizejumppads); + if (move.frames < 30) + { + bestareanum = 0; + bestareavolume = 0; + for (link = areas; link; link = link->next_area) + { + if (!AAS_AreaJumpPad(link->areanum)) continue; + volume = AAS_AreaVolume(link->areanum); + if (volume >= bestareavolume) + { + bestareanum = link->areanum; + bestareavolume = volume; + } //end if + } //end if + AAS_UnlinkFromAreas(areas); + return bestareanum; + } //end if + AAS_UnlinkFromAreas(areas); + } //end for + return 0; +} //end of the function AAS_BestReachableFromJumpPadArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin) +{ + int areanum, i, j, k, l; + aas_link_t *areas; + vec3_t absmins, absmaxs; + //vec3_t bbmins, bbmaxs; + vec3_t start, end; + aas_trace_t trace; + + if (!aasworld.loaded) + { + botimport.Print(PRT_ERROR, "AAS_BestReachableArea: aas not loaded\n"); + return 0; + } //end if + //find a point in an area + VectorCopy(origin, start); + areanum = AAS_PointAreaNum(start); + //while no area found fudge around a little + for (i = 0; i < 5 && !areanum; i++) + { + for (j = 0; j < 5 && !areanum; j++) + { + for (k = -1; k <= 1 && !areanum; k++) + { + for (l = -1; l <= 1 && !areanum; l++) + { + VectorCopy(origin, start); + start[0] += (float) j * 4 * k; + start[1] += (float) j * 4 * l; + start[2] += (float) i * 4; + areanum = AAS_PointAreaNum(start); + } //end for + } //end for + } //end for + } //end for + //if an area was found + if (areanum) + { + //drop client bbox down and try again + VectorCopy(start, end); + start[2] += 0.25; + end[2] -= 50; + trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); + if (!trace.startsolid) + { + areanum = AAS_PointAreaNum(trace.endpos); + VectorCopy(trace.endpos, goalorigin); + //FIXME: cannot enable next line right now because the reachability + // does not have to be calculated when the level items are loaded + //if the origin is in an area with reachability + //if (AAS_AreaReachability(areanum)) return areanum; + if (areanum) return areanum; + } //end if + else + { + //it can very well happen that the AAS_PointAreaNum function tells that + //a point is in an area and that starting a AAS_TraceClientBBox from that + //point will return trace.startsolid qtrue +#if 0 + if (AAS_PointAreaNum(start)) + { + Log_Write("point %f %f %f in area %d but trace startsolid", start[0], start[1], start[2], areanum); + AAS_DrawPermanentCross(start, 4, LINECOLOR_RED); + } //end if + botimport.Print(PRT_MESSAGE, "AAS_BestReachableArea: start solid\n"); +#endif + VectorCopy(start, goalorigin); + return areanum; + } //end else + } //end if + // + //AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs); + //NOTE: the goal origin does not have to be in the goal area + // because the bot will have to move towards the item origin anyway + VectorCopy(origin, goalorigin); + // + VectorAdd(origin, mins, absmins); + VectorAdd(origin, maxs, absmaxs); + //add bounding box size + //VectorSubtract(absmins, bbmaxs, absmins); + //VectorSubtract(absmaxs, bbmins, absmaxs); + //link an invalid (-1) entity + areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); + //get the reachable link arae + areanum = AAS_BestReachableLinkArea(areas); + //unlink the invalid entity + AAS_UnlinkFromAreas(areas); + // + return areanum; +} //end of the function AAS_BestReachableArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SetupReachabilityHeap(void) +{ + int i; + + reachabilityheap = (aas_lreachability_t *) GetClearedMemory( + AAS_MAX_REACHABILITYSIZE * sizeof(aas_lreachability_t)); + for (i = 0; i < AAS_MAX_REACHABILITYSIZE-1; i++) + { + reachabilityheap[i].next = &reachabilityheap[i+1]; + } //end for + reachabilityheap[AAS_MAX_REACHABILITYSIZE-1].next = NULL; + nextreachability = reachabilityheap; + numlreachabilities = 0; +} //end of the function AAS_InitReachabilityHeap +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShutDownReachabilityHeap(void) +{ + FreeMemory(reachabilityheap); + numlreachabilities = 0; +} //end of the function AAS_ShutDownReachabilityHeap +//=========================================================================== +// returns a reachability link +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_lreachability_t *AAS_AllocReachability(void) +{ + aas_lreachability_t *r; + + if (!nextreachability) return NULL; + //make sure the error message only shows up once + if (!nextreachability->next) AAS_Error("AAS_MAX_REACHABILITYSIZE"); + // + r = nextreachability; + nextreachability = nextreachability->next; + numlreachabilities++; + return r; +} //end of the function AAS_AllocReachability +//=========================================================================== +// frees a reachability link +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeReachability(aas_lreachability_t *lreach) +{ + Com_Memset(lreach, 0, sizeof(aas_lreachability_t)); + + lreach->next = nextreachability; + nextreachability = lreach; + numlreachabilities--; +} //end of the function AAS_FreeReachability +//=========================================================================== +// returns qtrue if the area has reachability links +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaReachability(int areanum) +{ + if (areanum < 0 || areanum >= aasworld.numareas) + { + AAS_Error("AAS_AreaReachability: areanum %d out of range", areanum); + return 0; + } //end if + return aasworld.areasettings[areanum].numreachableareas; +} //end of the function AAS_AreaReachability +//=========================================================================== +// returns the surface area of all ground faces together of the area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_AreaGroundFaceArea(int areanum) +{ + int i; + float total; + aas_area_t *area; + aas_face_t *face; + + total = 0; + area = &aasworld.areas[areanum]; + for (i = 0; i < area->numfaces; i++) + { + face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])]; + if (!(face->faceflags & FACE_GROUND)) continue; + // + total += AAS_FaceArea(face); + } //end for + return total; +} //end of the function AAS_AreaGroundFaceArea +//=========================================================================== +// returns the center of a face +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FaceCenter(int facenum, vec3_t center) +{ + int i; + float scale; + aas_face_t *face; + aas_edge_t *edge; + + face = &aasworld.faces[facenum]; + + VectorClear(center); + for (i = 0; i < face->numedges; i++) + { + edge = &aasworld.edges[abs(aasworld.edgeindex[face->firstedge + i])]; + VectorAdd(center, aasworld.vertexes[edge->v[0]], center); + VectorAdd(center, aasworld.vertexes[edge->v[1]], center); + } //end for + scale = 0.5 / face->numedges; + VectorScale(center, scale, center); +} //end of the function AAS_FaceCenter +//=========================================================================== +// returns the maximum distance a player can fall before being damaged +// damage = deltavelocity*deltavelocity * 0.0001 +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_FallDamageDistance(void) +{ + float maxzvelocity, gravity, t; + + maxzvelocity = sqrt(30 * 10000); + gravity = aassettings.phys_gravity; + t = maxzvelocity / gravity; + return 0.5 * gravity * t * t; +} //end of the function AAS_FallDamageDistance +//=========================================================================== +// distance = 0.5 * gravity * t * t +// vel = t * gravity +// damage = vel * vel * 0.0001 +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_FallDelta(float distance) +{ + float t, delta, gravity; + + gravity = aassettings.phys_gravity; + t = sqrt(fabs(distance) * 2 / gravity); + delta = t * gravity; + return delta * delta * 0.0001; +} //end of the function AAS_FallDelta +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_MaxJumpHeight(float phys_jumpvel) +{ + float phys_gravity; + + phys_gravity = aassettings.phys_gravity; + //maximum height a player can jump with the given initial z velocity + return 0.5 * phys_gravity * (phys_jumpvel / phys_gravity) * (phys_jumpvel / phys_gravity); +} //end of the function MaxJumpHeight +//=========================================================================== +// returns true if a player can only crouch in the area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_MaxJumpDistance(float phys_jumpvel) +{ + float phys_gravity, phys_maxvelocity, t; + + phys_gravity = aassettings.phys_gravity; + phys_maxvelocity = aassettings.phys_maxvelocity; + //time a player takes to fall the height + t = sqrt(aassettings.rs_maxjumpfallheight / (0.5 * phys_gravity)); + //maximum distance + return phys_maxvelocity * (t + phys_jumpvel / phys_gravity); +} //end of the function AAS_MaxJumpDistance +//=========================================================================== +// returns true if a player can only crouch in the area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaCrouch(int areanum) +{ + if (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qtrue; + else return qfalse; +} //end of the function AAS_AreaCrouch +//=========================================================================== +// returns qtrue if it is possible to swim in the area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaSwim(int areanum) +{ + if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue; + else return qfalse; +} //end of the function AAS_AreaSwim +//=========================================================================== +// returns qtrue if the area contains a liquid +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaLiquid(int areanum) +{ + if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue; + else return qfalse; +} //end of the function AAS_AreaLiquid +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaLava(int areanum) +{ + return (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA); +} //end of the function AAS_AreaLava +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaSlime(int areanum) +{ + return (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME); +} //end of the function AAS_AreaSlime +//=========================================================================== +// returns qtrue if the area contains ground faces +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaGrounded(int areanum) +{ + return (aasworld.areasettings[areanum].areaflags & AREA_GROUNDED); +} //end of the function AAS_AreaGround +//=========================================================================== +// returns true if the area contains ladder faces +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaLadder(int areanum) +{ + return (aasworld.areasettings[areanum].areaflags & AREA_LADDER); +} //end of the function AAS_AreaLadder +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaJumpPad(int areanum) +{ + return (aasworld.areasettings[areanum].contents & AREACONTENTS_JUMPPAD); +} //end of the function AAS_AreaJumpPad +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaTeleporter(int areanum) +{ + return (aasworld.areasettings[areanum].contents & AREACONTENTS_TELEPORTER); +} //end of the function AAS_AreaTeleporter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaClusterPortal(int areanum) +{ + return (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL); +} //end of the function AAS_AreaClusterPortal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaDoNotEnter(int areanum) +{ + return (aasworld.areasettings[areanum].contents & AREACONTENTS_DONOTENTER); +} //end of the function AAS_AreaDoNotEnter +//=========================================================================== +// returns the time it takes perform a barrier jump +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +unsigned short int AAS_BarrierJumpTravelTime(void) +{ + return aassettings.phys_jumpvel / (aassettings.phys_gravity * 0.1); +} //end op the function AAS_BarrierJumpTravelTime +//=========================================================================== +// returns true if there already exists a reachability from area1 to area2 +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_ReachabilityExists(int area1num, int area2num) +{ + aas_lreachability_t *r; + + for (r = areareachability[area1num]; r; r = r->next) + { + if (r->areanum == area2num) return qtrue; + } //end for + return qfalse; +} //end of the function AAS_ReachabilityExists +//=========================================================================== +// returns true if there is a solid just after the end point when going +// from start to end +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NearbySolidOrGap(vec3_t start, vec3_t end) +{ + vec3_t dir, testpoint; + int areanum; + + VectorSubtract(end, start, dir); + dir[2] = 0; + VectorNormalize(dir); + VectorMA(end, 48, dir, testpoint); + + areanum = AAS_PointAreaNum(testpoint); + if (!areanum) + { + testpoint[2] += 16; + areanum = AAS_PointAreaNum(testpoint); + if (!areanum) return qtrue; + } //end if + VectorMA(end, 64, dir, testpoint); + areanum = AAS_PointAreaNum(testpoint); + if (areanum) + { + if (!AAS_AreaSwim(areanum) && !AAS_AreaGrounded(areanum)) return qtrue; + } //end if + return qfalse; +} //end of the function AAS_SolidGapTime +//=========================================================================== +// searches for swim reachabilities between adjacent areas +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Reachability_Swim(int area1num, int area2num) +{ + int i, j, face1num, face2num, side1; + aas_area_t *area1, *area2; + aas_areasettings_t *areasettings; + aas_lreachability_t *lreach; + aas_face_t *face1; + aas_plane_t *plane; + vec3_t start; + + if (!AAS_AreaSwim(area1num) || !AAS_AreaSwim(area2num)) return qfalse; + //if the second area is crouch only + if (!(aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL)) return qfalse; + + area1 = &aasworld.areas[area1num]; + area2 = &aasworld.areas[area2num]; + + //if the areas are not near anough + for (i = 0; i < 3; i++) + { + if (area1->mins[i] > area2->maxs[i] + 10) return qfalse; + if (area1->maxs[i] < area2->mins[i] - 10) return qfalse; + } //end for + //find a shared face and create a reachability link + for (i = 0; i < area1->numfaces; i++) + { + face1num = aasworld.faceindex[area1->firstface + i]; + side1 = face1num < 0; + face1num = abs(face1num); + // + for (j = 0; j < area2->numfaces; j++) + { + face2num = abs(aasworld.faceindex[area2->firstface + j]); + // + if (face1num == face2num) + { + AAS_FaceCenter(face1num, start); + // + if (AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) + { + // + face1 = &aasworld.faces[face1num]; + areasettings = &aasworld.areasettings[area1num]; + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = face1num; + lreach->edgenum = 0; + VectorCopy(start, lreach->start); + plane = &aasworld.planes[face1->planenum ^ side1]; + VectorMA(lreach->start, -INSIDEUNITS, plane->normal, lreach->end); + lreach->traveltype = TRAVEL_SWIM; + lreach->traveltime = 1; + //if the volume of the area is rather small + if (AAS_AreaVolume(area2num) < 800) + lreach->traveltime += 200; + //if (!(AAS_PointContents(start) & MASK_WATER)) lreach->traveltime += 500; + //link the reachability + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + reach_swim++; + return qtrue; + } //end if + } //end if + } //end for + } //end for + return qfalse; +} //end of the function AAS_Reachability_Swim +//=========================================================================== +// searches for reachabilities between adjacent areas with equal floor +// heights +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Reachability_EqualFloorHeight(int area1num, int area2num) +{ + int i, j, edgenum, edgenum1, edgenum2, foundreach, side; + float height, bestheight, length, bestlength; + vec3_t dir, start, end, normal, invgravity, gravitydirection = {0, 0, -1}; + vec3_t edgevec; + aas_area_t *area1, *area2; + aas_face_t *face1, *face2; + aas_edge_t *edge; + aas_plane_t *plane2; + aas_lreachability_t lr, *lreach; + + if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse; + + area1 = &aasworld.areas[area1num]; + area2 = &aasworld.areas[area2num]; + //if the areas are not near anough in the x-y direction + for (i = 0; i < 2; i++) + { + if (area1->mins[i] > area2->maxs[i] + 10) return qfalse; + if (area1->maxs[i] < area2->mins[i] - 10) return qfalse; + } //end for + //if area 2 is too high above area 1 + if (area2->mins[2] > area1->maxs[2]) return qfalse; + // + VectorCopy(gravitydirection, invgravity); + VectorInverse(invgravity); + // + bestheight = 99999; + bestlength = 0; + foundreach = qfalse; + Com_Memset(&lr, 0, sizeof(aas_lreachability_t)); //make the compiler happy + // + //check if the areas have ground faces with a common edge + //if existing use the lowest common edge for a reachability link + for (i = 0; i < area1->numfaces; i++) + { + face1 = &aasworld.faces[abs(aasworld.faceindex[area1->firstface + i])]; + if (!(face1->faceflags & FACE_GROUND)) continue; + // + for (j = 0; j < area2->numfaces; j++) + { + face2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])]; + if (!(face2->faceflags & FACE_GROUND)) continue; + //if there is a common edge + for (edgenum1 = 0; edgenum1 < face1->numedges; edgenum1++) + { + for (edgenum2 = 0; edgenum2 < face2->numedges; edgenum2++) + { + if (abs(aasworld.edgeindex[face1->firstedge + edgenum1]) != + abs(aasworld.edgeindex[face2->firstedge + edgenum2])) + continue; + edgenum = aasworld.edgeindex[face1->firstedge + edgenum1]; + side = edgenum < 0; + edge = &aasworld.edges[abs(edgenum)]; + //get the length of the edge + VectorSubtract(aasworld.vertexes[edge->v[1]], + aasworld.vertexes[edge->v[0]], dir); + length = VectorLength(dir); + //get the start point + VectorAdd(aasworld.vertexes[edge->v[0]], + aasworld.vertexes[edge->v[1]], start); + VectorScale(start, 0.5, start); + VectorCopy(start, end); + //get the end point several units inside area2 + //and the start point several units inside area1 + //NOTE: normal is pointing into area2 because the + //face edges are stored counter clockwise + VectorSubtract(aasworld.vertexes[edge->v[side]], + aasworld.vertexes[edge->v[!side]], edgevec); + plane2 = &aasworld.planes[face2->planenum]; + CrossProduct(edgevec, plane2->normal, normal); + VectorNormalize(normal); + // + //VectorMA(start, -1, normal, start); + VectorMA(end, INSIDEUNITS_WALKEND, normal, end); + VectorMA(start, INSIDEUNITS_WALKSTART, normal, start); + end[2] += 0.125; + // + height = DotProduct(invgravity, start); + //NOTE: if there's nearby solid or a gap area after this area + //disabled this crap + //if (AAS_NearbySolidOrGap(start, end)) height += 200; + //NOTE: disabled because it disables reachabilities to very small areas + //if (AAS_PointAreaNum(end) != area2num) continue; + //get the longest lowest edge + if (height < bestheight || + (height < bestheight + 1 && length > bestlength)) + { + bestheight = height; + bestlength = length; + //create a new reachability link + lr.areanum = area2num; + lr.facenum = 0; + lr.edgenum = edgenum; + VectorCopy(start, lr.start); + VectorCopy(end, lr.end); + lr.traveltype = TRAVEL_WALK; + lr.traveltime = 1; + foundreach = qtrue; + } //end if + } //end for + } //end for + } //end for + } //end for + if (foundreach) + { + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = lr.areanum; + lreach->facenum = lr.facenum; + lreach->edgenum = lr.edgenum; + VectorCopy(lr.start, lreach->start); + VectorCopy(lr.end, lreach->end); + lreach->traveltype = lr.traveltype; + lreach->traveltime = lr.traveltime; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + //if going into a crouch area + if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num)) + { + lreach->traveltime += aassettings.rs_startcrouch; + } //end if + /* + //NOTE: if there's nearby solid or a gap area after this area + if (!AAS_NearbySolidOrGap(lreach->start, lreach->end)) + { + lreach->traveltime += 100; + } //end if + */ + //avoid rather small areas + //if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100; + // + reach_equalfloor++; + return qtrue; + } //end if + return qfalse; +} //end of the function AAS_Reachability_EqualFloorHeight +//=========================================================================== +// searches step, barrier, waterjump and walk off ledge reachabilities +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(int area1num, int area2num) +{ + int i, j, k, l, edge1num, edge2num, areas[10], numareas; + int ground_bestarea2groundedgenum, ground_foundreach; + int water_bestarea2groundedgenum, water_foundreach; + int side1, area1swim, faceside1, groundface1num; + float dist, dist1, dist2, diff, invgravitydot, ortdot; + float x1, x2, x3, x4, y1, y2, y3, y4, tmp, y; + float length, ground_bestlength, water_bestlength, ground_bestdist, water_bestdist; + vec3_t v1, v2, v3, v4, tmpv, p1area1, p1area2, p2area1, p2area2; + vec3_t normal, ort, edgevec, start, end, dir; + vec3_t ground_beststart = {0, 0, 0}, ground_bestend = {0, 0, 0}, ground_bestnormal = {0, 0, 0}; + vec3_t water_beststart = {0, 0, 0}, water_bestend = {0, 0, 0}, water_bestnormal = {0, 0, 0}; + vec3_t invgravity = {0, 0, 1}; + vec3_t testpoint; + aas_plane_t *plane; + aas_area_t *area1, *area2; + aas_face_t *groundface1, *groundface2, *ground_bestface1, *water_bestface1; + aas_edge_t *edge1, *edge2; + aas_lreachability_t *lreach; + aas_trace_t trace; + + //must be able to walk or swim in the first area + if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse; + // + if (!AAS_AreaGrounded(area2num) && !AAS_AreaSwim(area2num)) return qfalse; + // + area1 = &aasworld.areas[area1num]; + area2 = &aasworld.areas[area2num]; + //if the first area contains a liquid + area1swim = AAS_AreaSwim(area1num); + //if the areas are not near anough in the x-y direction + for (i = 0; i < 2; i++) + { + if (area1->mins[i] > area2->maxs[i] + 10) return qfalse; + if (area1->maxs[i] < area2->mins[i] - 10) return qfalse; + } //end for + // + ground_foundreach = qfalse; + ground_bestdist = 99999; + ground_bestlength = 0; + ground_bestarea2groundedgenum = 0; + // + water_foundreach = qfalse; + water_bestdist = 99999; + water_bestlength = 0; + water_bestarea2groundedgenum = 0; + // + for (i = 0; i < area1->numfaces; i++) + { + groundface1num = aasworld.faceindex[area1->firstface + i]; + faceside1 = groundface1num < 0; + groundface1 = &aasworld.faces[abs(groundface1num)]; + //if this isn't a ground face + if (!(groundface1->faceflags & FACE_GROUND)) + { + //if we can swim in the first area + if (area1swim) + { + //face plane must be more or less horizontal + plane = &aasworld.planes[groundface1->planenum ^ (!faceside1)]; + if (DotProduct(plane->normal, invgravity) < 0.7) continue; + } //end if + else + { + //if we can't swim in the area it must be a ground face + continue; + } //end else + } //end if + // + for (k = 0; k < groundface1->numedges; k++) + { + edge1num = aasworld.edgeindex[groundface1->firstedge + k]; + side1 = (edge1num < 0); + //NOTE: for water faces we must take the side area 1 is + // on into account because the face is shared and doesn't + // have to be oriented correctly + if (!(groundface1->faceflags & FACE_GROUND)) side1 = (side1 == faceside1); + edge1num = abs(edge1num); + edge1 = &aasworld.edges[edge1num]; + //vertexes of the edge + VectorCopy(aasworld.vertexes[edge1->v[!side1]], v1); + VectorCopy(aasworld.vertexes[edge1->v[side1]], v2); + //get a vertical plane through the edge + //NOTE: normal is pointing into area 2 because the + //face edges are stored counter clockwise + VectorSubtract(v2, v1, edgevec); + CrossProduct(edgevec, invgravity, normal); + VectorNormalize(normal); + dist = DotProduct(normal, v1); + //check the faces from the second area + for (j = 0; j < area2->numfaces; j++) + { + groundface2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])]; + //must be a ground face + if (!(groundface2->faceflags & FACE_GROUND)) continue; + //check the edges of this ground face + for (l = 0; l < groundface2->numedges; l++) + { + edge2num = abs(aasworld.edgeindex[groundface2->firstedge + l]); + edge2 = &aasworld.edges[edge2num]; + //vertexes of the edge + VectorCopy(aasworld.vertexes[edge2->v[0]], v3); + VectorCopy(aasworld.vertexes[edge2->v[1]], v4); + //check the distance between the two points and the vertical plane + //through the edge of area1 + diff = DotProduct(normal, v3) - dist; + if (diff < -0.1 || diff > 0.1) continue; + diff = DotProduct(normal, v4) - dist; + if (diff < -0.1 || diff > 0.1) continue; + // + //project the two ground edges into the step side plane + //and calculate the shortest distance between the two + //edges if they overlap in the direction orthogonal to + //the gravity direction + CrossProduct(invgravity, normal, ort); + invgravitydot = DotProduct(invgravity, invgravity); + ortdot = DotProduct(ort, ort); + //projection into the step plane + //NOTE: since gravity is vertical this is just the z coordinate + y1 = v1[2];//DotProduct(v1, invgravity) / invgravitydot; + y2 = v2[2];//DotProduct(v2, invgravity) / invgravitydot; + y3 = v3[2];//DotProduct(v3, invgravity) / invgravitydot; + y4 = v4[2];//DotProduct(v4, invgravity) / invgravitydot; + // + x1 = DotProduct(v1, ort) / ortdot; + x2 = DotProduct(v2, ort) / ortdot; + x3 = DotProduct(v3, ort) / ortdot; + x4 = DotProduct(v4, ort) / ortdot; + // + if (x1 > x2) + { + tmp = x1; x1 = x2; x2 = tmp; + tmp = y1; y1 = y2; y2 = tmp; + VectorCopy(v1, tmpv); VectorCopy(v2, v1); VectorCopy(tmpv, v2); + } //end if + if (x3 > x4) + { + tmp = x3; x3 = x4; x4 = tmp; + tmp = y3; y3 = y4; y4 = tmp; + VectorCopy(v3, tmpv); VectorCopy(v4, v3); VectorCopy(tmpv, v4); + } //end if + //if the two projected edge lines have no overlap + if (x2 <= x3 || x4 <= x1) + { +// Log_Write("lines no overlap: from area %d to %d\r\n", area1num, area2num); + continue; + } //end if + //if the two lines fully overlap + if ((x1 - 0.5 < x3 && x4 < x2 + 0.5) && + (x3 - 0.5 < x1 && x2 < x4 + 0.5)) + { + dist1 = y3 - y1; + dist2 = y4 - y2; + VectorCopy(v1, p1area1); + VectorCopy(v2, p2area1); + VectorCopy(v3, p1area2); + VectorCopy(v4, p2area2); + } //end if + else + { + //if the points are equal + if (x1 > x3 - 0.1 && x1 < x3 + 0.1) + { + dist1 = y3 - y1; + VectorCopy(v1, p1area1); + VectorCopy(v3, p1area2); + } //end if + else if (x1 < x3) + { + y = y1 + (x3 - x1) * (y2 - y1) / (x2 - x1); + dist1 = y3 - y; + VectorCopy(v3, p1area1); + p1area1[2] = y; + VectorCopy(v3, p1area2); + } //end if + else + { + y = y3 + (x1 - x3) * (y4 - y3) / (x4 - x3); + dist1 = y - y1; + VectorCopy(v1, p1area1); + VectorCopy(v1, p1area2); + p1area2[2] = y; + } //end if + //if the points are equal + if (x2 > x4 - 0.1 && x2 < x4 + 0.1) + { + dist2 = y4 - y2; + VectorCopy(v2, p2area1); + VectorCopy(v4, p2area2); + } //end if + else if (x2 < x4) + { + y = y3 + (x2 - x3) * (y4 - y3) / (x4 - x3); + dist2 = y - y2; + VectorCopy(v2, p2area1); + VectorCopy(v2, p2area2); + p2area2[2] = y; + } //end if + else + { + y = y1 + (x4 - x1) * (y2 - y1) / (x2 - x1); + dist2 = y4 - y; + VectorCopy(v4, p2area1); + p2area1[2] = y; + VectorCopy(v4, p2area2); + } //end else + } //end else + //if both distances are pretty much equal + //then we take the middle of the points + if (dist1 > dist2 - 1 && dist1 < dist2 + 1) + { + dist = dist1; + VectorAdd(p1area1, p2area1, start); + VectorScale(start, 0.5, start); + VectorAdd(p1area2, p2area2, end); + VectorScale(end, 0.5, end); + } //end if + else if (dist1 < dist2) + { + dist = dist1; + VectorCopy(p1area1, start); + VectorCopy(p1area2, end); + } //end else if + else + { + dist = dist2; + VectorCopy(p2area1, start); + VectorCopy(p2area2, end); + } //end else + //get the length of the overlapping part of the edges of the two areas + VectorSubtract(p2area2, p1area2, dir); + length = VectorLength(dir); + // + if (groundface1->faceflags & FACE_GROUND) + { + //if the vertical distance is smaller + if (dist < ground_bestdist || + //or the vertical distance is pretty much the same + //but the overlapping part of the edges is longer + (dist < ground_bestdist + 1 && length > ground_bestlength)) + { + ground_bestdist = dist; + ground_bestlength = length; + ground_foundreach = qtrue; + ground_bestarea2groundedgenum = edge1num; + ground_bestface1 = groundface1; + //best point towards area1 + VectorCopy(start, ground_beststart); + //normal is pointing into area2 + VectorCopy(normal, ground_bestnormal); + //best point towards area2 + VectorCopy(end, ground_bestend); + } //end if + } //end if + else + { + //if the vertical distance is smaller + if (dist < water_bestdist || + //or the vertical distance is pretty much the same + //but the overlapping part of the edges is longer + (dist < water_bestdist + 1 && length > water_bestlength)) + { + water_bestdist = dist; + water_bestlength = length; + water_foundreach = qtrue; + water_bestarea2groundedgenum = edge1num; + water_bestface1 = groundface1; + //best point towards area1 + VectorCopy(start, water_beststart); + //normal is pointing into area2 + VectorCopy(normal, water_bestnormal); + //best point towards area2 + VectorCopy(end, water_bestend); + } //end if + } //end else + } //end for + } //end for + } //end for + } //end for + // + // NOTE: swim reachabilities are already filtered out + // + // Steps + // + // --------- + // | step height -> TRAVEL_WALK + //--------| + // + // --------- + //~~~~~~~~| step height and low water -> TRAVEL_WALK + //--------| + // + //~~~~~~~~~~~~~~~~~~ + // --------- + // | step height and low water up to the step -> TRAVEL_WALK + //--------| + // + //check for a step reachability + if (ground_foundreach) + { + //if area2 is higher but lower than the maximum step height + //NOTE: ground_bestdist >= 0 also catches equal floor reachabilities + if (ground_bestdist >= 0 && ground_bestdist < aassettings.phys_maxstep) + { + //create walk reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = ground_bestarea2groundedgenum; + VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start); + VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); + lreach->traveltype = TRAVEL_WALK; + lreach->traveltime = 0;//1; + //if going into a crouch area + if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num)) + { + lreach->traveltime += aassettings.rs_startcrouch; + } //end if + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + //NOTE: if there's nearby solid or a gap area after this area + /* + if (!AAS_NearbySolidOrGap(lreach->start, lreach->end)) + { + lreach->traveltime += 100; + } //end if + */ + //avoid rather small areas + //if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100; + // + reach_step++; + return qtrue; + } //end if + } //end if + // + // Water Jumps + // + // --------- + // | + //~~~~~~~~| + // | + // | higher than step height and water up to waterjump height -> TRAVEL_WATERJUMP + //--------| + // + //~~~~~~~~~~~~~~~~~~ + // --------- + // | + // | + // | + // | higher than step height and low water up to the step -> TRAVEL_WATERJUMP + //--------| + // + //check for a waterjump reachability + if (water_foundreach) + { + //get a test point a little bit towards area1 + VectorMA(water_bestend, -INSIDEUNITS, water_bestnormal, testpoint); + //go down the maximum waterjump height + testpoint[2] -= aassettings.phys_maxwaterjump; + //if there IS water the sv_maxwaterjump height below the bestend point + if (aasworld.areasettings[AAS_PointAreaNum(testpoint)].areaflags & AREA_LIQUID) + { + //don't create rediculous water jump reachabilities from areas very far below + //the water surface + if (water_bestdist < aassettings.phys_maxwaterjump + 24) + { + //waterjumping from or towards a crouch only area is not possible in Quake2 + if ((aasworld.areasettings[area1num].presencetype & PRESENCE_NORMAL) && + (aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL)) + { + //create water jump reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = water_bestarea2groundedgenum; + VectorCopy(water_beststart, lreach->start); + VectorMA(water_bestend, INSIDEUNITS_WATERJUMP, water_bestnormal, lreach->end); + lreach->traveltype = TRAVEL_WATERJUMP; + lreach->traveltime = aassettings.rs_waterjump; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + //we've got another waterjump reachability + reach_waterjump++; + return qtrue; + } //end if + } //end if + } //end if + } //end if + // + // Barrier Jumps + // + // --------- + // | + // | + // | + // | higher than step height lower than barrier height -> TRAVEL_BARRIERJUMP + //--------| + // + // --------- + // | + // | + // | + //~~~~~~~~| higher than step height lower than barrier height + //--------| and a thin layer of water in the area to jump from -> TRAVEL_BARRIERJUMP + // + //check for a barrier jump reachability + if (ground_foundreach) + { + //if area2 is higher but lower than the maximum barrier jump height + if (ground_bestdist > 0 && ground_bestdist < aassettings.phys_maxbarrier) + { + //if no water in area1 or a very thin layer of water on the ground + if (!water_foundreach || (ground_bestdist - water_bestdist < 16)) + { + //cannot perform a barrier jump towards or from a crouch area in Quake2 + if (!AAS_AreaCrouch(area1num) && !AAS_AreaCrouch(area2num)) + { + //create barrier jump reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = ground_bestarea2groundedgenum; + VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start); + VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); + lreach->traveltype = TRAVEL_BARRIERJUMP; + lreach->traveltime = aassettings.rs_barrierjump;//AAS_BarrierJumpTravelTime(); + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + //we've got another barrierjump reachability + reach_barrier++; + return qtrue; + } //end if + } //end if + } //end if + } //end if + // + // Walk and Walk Off Ledge + // + //--------| + // | can walk or step back -> TRAVEL_WALK + // --------- + // + //--------| + // | + // | + // | + // | cannot walk/step back -> TRAVEL_WALKOFFLEDGE + // --------- + // + //--------| + // | + // |~~~~~~~~ + // | + // | cannot step back but can waterjump back -> TRAVEL_WALKOFFLEDGE + // --------- FIXME: create TRAVEL_WALK reach?? + // + //check for a walk or walk off ledge reachability + if (ground_foundreach) + { + if (ground_bestdist < 0) + { + if (ground_bestdist > -aassettings.phys_maxstep) + { + //create walk reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = ground_bestarea2groundedgenum; + VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start); + VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); + lreach->traveltype = TRAVEL_WALK; + lreach->traveltime = 1; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + //we've got another walk reachability + reach_walk++; + return qtrue; + } //end if + // if no maximum fall height set or less than the max + if (!aassettings.rs_maxfallheight || fabs(ground_bestdist) < aassettings.rs_maxfallheight) { + //trace a bounding box vertically to check for solids + VectorMA(ground_bestend, INSIDEUNITS, ground_bestnormal, ground_bestend); + VectorCopy(ground_bestend, start); + start[2] = ground_beststart[2]; + VectorCopy(ground_bestend, end); + end[2] += 4; + trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); + //if no solids were found + if (!trace.startsolid && trace.fraction >= 1.0) + { + //the trace end point must be in the goal area + trace.endpos[2] += 1; + if (AAS_PointAreaNum(trace.endpos) == area2num) + { + //if not going through a cluster portal + numareas = AAS_TraceAreas(start, end, areas, NULL, sizeof(areas) / sizeof(int)); + for (i = 0; i < numareas; i++) + if (AAS_AreaClusterPortal(areas[i])) + break; + if (i >= numareas) + { + //create a walk off ledge reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = ground_bestarea2groundedgenum; + VectorCopy(ground_beststart, lreach->start); + VectorCopy(ground_bestend, lreach->end); + lreach->traveltype = TRAVEL_WALKOFFLEDGE; + lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(ground_bestdist) * 50 / aassettings.phys_gravity; + //if falling from too high and not falling into water + if (!AAS_AreaSwim(area2num) && !AAS_AreaJumpPad(area2num)) + { + if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta5) + { + lreach->traveltime += aassettings.rs_falldamage5; + } //end if + if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta10) + { + lreach->traveltime += aassettings.rs_falldamage10; + } //end if + } //end if + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + reach_walkoffledge++; + //NOTE: don't create a weapon (rl, bfg) jump reachability here + //because it interferes with other reachabilities + //like the ladder reachability + return qtrue; + } //end if + } //end if + } //end if + } //end if + } //end else + } //end if + return qfalse; +} //end of the function AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge +//=========================================================================== +// returns the distance between the two vectors +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float VectorDistance(vec3_t v1, vec3_t v2) +{ + vec3_t dir; + + VectorSubtract(v2, v1, dir); + return VectorLength(dir); +} //end of the function VectorDistance +//=========================================================================== +// returns true if the first vector is between the last two vectors +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int VectorBetweenVectors(vec3_t v, vec3_t v1, vec3_t v2) +{ + vec3_t dir1, dir2; + + VectorSubtract(v, v1, dir1); + VectorSubtract(v, v2, dir2); + return (DotProduct(dir1, dir2) <= 0); +} //end of the function VectorBetweenVectors +//=========================================================================== +// returns the mid point between the two vectors +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void VectorMiddle(vec3_t v1, vec3_t v2, vec3_t middle) +{ + VectorAdd(v1, v2, middle); + VectorScale(middle, 0.5, middle); +} //end of the function VectorMiddle +//=========================================================================== +// calculate a range of points closest to each other on both edges +// +// Parameter: beststart1 start of the range of points on edge v1-v2 +// beststart2 end of the range of points on edge v1-v2 +// bestend1 start of the range of points on edge v3-v4 +// bestend2 end of the range of points on edge v3-v4 +// bestdist best distance so far +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4, + aas_plane_t *plane1, aas_plane_t *plane2, + vec3_t beststart, vec3_t bestend, float bestdist) +{ + vec3_t dir1, dir2, p1, p2, p3, p4; + float a1, a2, b1, b2, dist; + int founddist; + + //edge vectors + VectorSubtract(v2, v1, dir1); + VectorSubtract(v4, v3, dir2); + //get the horizontal directions + dir1[2] = 0; + dir2[2] = 0; + // + // p1 = point on an edge vector of area2 closest to v1 + // p2 = point on an edge vector of area2 closest to v2 + // p3 = point on an edge vector of area1 closest to v3 + // p4 = point on an edge vector of area1 closest to v4 + // + if (dir2[0]) + { + a2 = dir2[1] / dir2[0]; + b2 = v3[1] - a2 * v3[0]; + //point on the edge vector of area2 closest to v1 + p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; + p1[1] = a2 * p1[0] + b2; + //point on the edge vector of area2 closest to v2 + p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; + p2[1] = a2 * p2[0] + b2; + } //end if + else + { + //point on the edge vector of area2 closest to v1 + p1[0] = v3[0]; + p1[1] = v1[1]; + //point on the edge vector of area2 closest to v2 + p2[0] = v3[0]; + p2[1] = v2[1]; + } //end else + // + if (dir1[0]) + { + // + a1 = dir1[1] / dir1[0]; + b1 = v1[1] - a1 * v1[0]; + //point on the edge vector of area1 closest to v3 + p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; + p3[1] = a1 * p3[0] + b1; + //point on the edge vector of area1 closest to v4 + p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; + p4[1] = a1 * p4[0] + b1; + } //end if + else + { + //point on the edge vector of area1 closest to v3 + p3[0] = v1[0]; + p3[1] = v3[1]; + //point on the edge vector of area1 closest to v4 + p4[0] = v1[0]; + p4[1] = v4[1]; + } //end else + //start with zero z-coordinates + p1[2] = 0; + p2[2] = 0; + p3[2] = 0; + p4[2] = 0; + //calculate the z-coordinates from the ground planes + p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2]; + p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2]; + p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2]; + p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2]; + // + founddist = qfalse; + // + if (VectorBetweenVectors(p1, v3, v4)) + { + dist = VectorDistance(v1, p1); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + VectorMiddle(beststart, v1, beststart); + VectorMiddle(bestend, p1, bestend); + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v1, beststart); + VectorCopy(p1, bestend); + } //end if + founddist = qtrue; + } //end if + if (VectorBetweenVectors(p2, v3, v4)) + { + dist = VectorDistance(v2, p2); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + VectorMiddle(beststart, v2, beststart); + VectorMiddle(bestend, p2, bestend); + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v2, beststart); + VectorCopy(p2, bestend); + } //end if + founddist = qtrue; + } //end else if + if (VectorBetweenVectors(p3, v1, v2)) + { + dist = VectorDistance(v3, p3); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + VectorMiddle(beststart, p3, beststart); + VectorMiddle(bestend, v3, bestend); + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(p3, beststart); + VectorCopy(v3, bestend); + } //end if + founddist = qtrue; + } //end else if + if (VectorBetweenVectors(p4, v1, v2)) + { + dist = VectorDistance(v4, p4); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + VectorMiddle(beststart, p4, beststart); + VectorMiddle(bestend, v4, bestend); + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(p4, beststart); + VectorCopy(v4, bestend); + } //end if + founddist = qtrue; + } //end else if + //if no shortest distance was found the shortest distance + //is between one of the vertexes of edge1 and one of edge2 + if (!founddist) + { + dist = VectorDistance(v1, v3); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v1, beststart); + VectorCopy(v3, bestend); + } //end if + dist = VectorDistance(v1, v4); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v1, beststart); + VectorCopy(v4, bestend); + } //end if + dist = VectorDistance(v2, v3); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v2, beststart); + VectorCopy(v3, bestend); + } //end if + dist = VectorDistance(v2, v4); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v2, beststart); + VectorCopy(v4, bestend); + } //end if + } //end if + return bestdist; +} //end of the function AAS_ClosestEdgePoints*/ + +float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4, + aas_plane_t *plane1, aas_plane_t *plane2, + vec3_t beststart1, vec3_t bestend1, + vec3_t beststart2, vec3_t bestend2, float bestdist) +{ + vec3_t dir1, dir2, p1, p2, p3, p4; + float a1, a2, b1, b2, dist, dist1, dist2; + int founddist; + + //edge vectors + VectorSubtract(v2, v1, dir1); + VectorSubtract(v4, v3, dir2); + //get the horizontal directions + dir1[2] = 0; + dir2[2] = 0; + // + // p1 = point on an edge vector of area2 closest to v1 + // p2 = point on an edge vector of area2 closest to v2 + // p3 = point on an edge vector of area1 closest to v3 + // p4 = point on an edge vector of area1 closest to v4 + // + if (dir2[0]) + { + a2 = dir2[1] / dir2[0]; + b2 = v3[1] - a2 * v3[0]; + //point on the edge vector of area2 closest to v1 + p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; + p1[1] = a2 * p1[0] + b2; + //point on the edge vector of area2 closest to v2 + p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; + p2[1] = a2 * p2[0] + b2; + } //end if + else + { + //point on the edge vector of area2 closest to v1 + p1[0] = v3[0]; + p1[1] = v1[1]; + //point on the edge vector of area2 closest to v2 + p2[0] = v3[0]; + p2[1] = v2[1]; + } //end else + // + if (dir1[0]) + { + // + a1 = dir1[1] / dir1[0]; + b1 = v1[1] - a1 * v1[0]; + //point on the edge vector of area1 closest to v3 + p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; + p3[1] = a1 * p3[0] + b1; + //point on the edge vector of area1 closest to v4 + p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; + p4[1] = a1 * p4[0] + b1; + } //end if + else + { + //point on the edge vector of area1 closest to v3 + p3[0] = v1[0]; + p3[1] = v3[1]; + //point on the edge vector of area1 closest to v4 + p4[0] = v1[0]; + p4[1] = v4[1]; + } //end else + //start with zero z-coordinates + p1[2] = 0; + p2[2] = 0; + p3[2] = 0; + p4[2] = 0; + //calculate the z-coordinates from the ground planes + p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2]; + p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2]; + p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2]; + p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2]; + // + founddist = qfalse; + // + if (VectorBetweenVectors(p1, v3, v4)) + { + dist = VectorDistance(v1, p1); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + dist1 = VectorDistance(beststart1, v1); + dist2 = VectorDistance(beststart2, v1); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart2); + } //end if + else + { + if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart1); + } //end else + dist1 = VectorDistance(bestend1, p1); + dist2 = VectorDistance(bestend2, p1); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend2); + } //end if + else + { + if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend1); + } //end else + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v1, beststart1); + VectorCopy(v1, beststart2); + VectorCopy(p1, bestend1); + VectorCopy(p1, bestend2); + } //end if + founddist = qtrue; + } //end if + if (VectorBetweenVectors(p2, v3, v4)) + { + dist = VectorDistance(v2, p2); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + dist1 = VectorDistance(beststart1, v2); + dist2 = VectorDistance(beststart2, v2); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart2); + } //end if + else + { + if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart1); + } //end else + dist1 = VectorDistance(bestend1, p2); + dist2 = VectorDistance(bestend2, p2); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend2); + } //end if + else + { + if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend1); + } //end else + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v2, beststart1); + VectorCopy(v2, beststart2); + VectorCopy(p2, bestend1); + VectorCopy(p2, bestend2); + } //end if + founddist = qtrue; + } //end else if + if (VectorBetweenVectors(p3, v1, v2)) + { + dist = VectorDistance(v3, p3); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + dist1 = VectorDistance(beststart1, p3); + dist2 = VectorDistance(beststart2, p3); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart2); + } //end if + else + { + if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart1); + } //end else + dist1 = VectorDistance(bestend1, v3); + dist2 = VectorDistance(bestend2, v3); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend2); + } //end if + else + { + if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend1); + } //end else + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(p3, beststart1); + VectorCopy(p3, beststart2); + VectorCopy(v3, bestend1); + VectorCopy(v3, bestend2); + } //end if + founddist = qtrue; + } //end else if + if (VectorBetweenVectors(p4, v1, v2)) + { + dist = VectorDistance(v4, p4); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + dist1 = VectorDistance(beststart1, p4); + dist2 = VectorDistance(beststart2, p4); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart2); + } //end if + else + { + if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart1); + } //end else + dist1 = VectorDistance(bestend1, v4); + dist2 = VectorDistance(bestend2, v4); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend2); + } //end if + else + { + if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend1); + } //end else + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(p4, beststart1); + VectorCopy(p4, beststart2); + VectorCopy(v4, bestend1); + VectorCopy(v4, bestend2); + } //end if + founddist = qtrue; + } //end else if + //if no shortest distance was found the shortest distance + //is between one of the vertexes of edge1 and one of edge2 + if (!founddist) + { + dist = VectorDistance(v1, v3); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v1, beststart1); + VectorCopy(v1, beststart2); + VectorCopy(v3, bestend1); + VectorCopy(v3, bestend2); + } //end if + dist = VectorDistance(v1, v4); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v1, beststart1); + VectorCopy(v1, beststart2); + VectorCopy(v4, bestend1); + VectorCopy(v4, bestend2); + } //end if + dist = VectorDistance(v2, v3); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v2, beststart1); + VectorCopy(v2, beststart2); + VectorCopy(v3, bestend1); + VectorCopy(v3, bestend2); + } //end if + dist = VectorDistance(v2, v4); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v2, beststart1); + VectorCopy(v2, beststart2); + VectorCopy(v4, bestend1); + VectorCopy(v4, bestend2); + } //end if + } //end if + return bestdist; +} //end of the function AAS_ClosestEdgePoints +//=========================================================================== +// creates possible jump reachabilities between the areas +// +// The two closest points on the ground of the areas are calculated +// One of the points will be on an edge of a ground face of area1 and +// one on an edge of a ground face of area2. +// If there is a range of closest points the point in the middle of this range +// is selected. +// Between these two points there must be one or more gaps. +// If the gaps exist a potential jump is predicted. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Reachability_Jump(int area1num, int area2num) +{ + int i, j, k, l, face1num, face2num, edge1num, edge2num, traveltype; + int stopevent, areas[10], numareas; + float phys_jumpvel, maxjumpdistance, maxjumpheight, height, bestdist, speed; + vec_t *v1, *v2, *v3, *v4; + vec3_t beststart, beststart2, bestend, bestend2; + vec3_t teststart, testend, dir, velocity, cmdmove, up = {0, 0, 1}, sidewards; + aas_area_t *area1, *area2; + aas_face_t *face1, *face2; + aas_edge_t *edge1, *edge2; + aas_plane_t *plane1, *plane2, *plane; + aas_trace_t trace; + aas_clientmove_t move; + aas_lreachability_t *lreach; + + if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse; + //cannot jump from or to a crouch area + if (AAS_AreaCrouch(area1num) || AAS_AreaCrouch(area2num)) return qfalse; + // + area1 = &aasworld.areas[area1num]; + area2 = &aasworld.areas[area2num]; + // + phys_jumpvel = aassettings.phys_jumpvel; + //maximum distance a player can jump + maxjumpdistance = 2 * AAS_MaxJumpDistance(phys_jumpvel); + //maximum height a player can jump with the given initial z velocity + maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel); + + //if the areas are not near anough in the x-y direction + for (i = 0; i < 2; i++) + { + if (area1->mins[i] > area2->maxs[i] + maxjumpdistance) return qfalse; + if (area1->maxs[i] < area2->mins[i] - maxjumpdistance) return qfalse; + } //end for + //if area2 is way to high to jump up to + if (area2->mins[2] > area1->maxs[2] + maxjumpheight) return qfalse; + // + bestdist = 999999; + // + for (i = 0; i < area1->numfaces; i++) + { + face1num = aasworld.faceindex[area1->firstface + i]; + face1 = &aasworld.faces[abs(face1num)]; + //if not a ground face + if (!(face1->faceflags & FACE_GROUND)) continue; + // + for (j = 0; j < area2->numfaces; j++) + { + face2num = aasworld.faceindex[area2->firstface + j]; + face2 = &aasworld.faces[abs(face2num)]; + //if not a ground face + if (!(face2->faceflags & FACE_GROUND)) continue; + // + for (k = 0; k < face1->numedges; k++) + { + edge1num = abs(aasworld.edgeindex[face1->firstedge + k]); + edge1 = &aasworld.edges[edge1num]; + for (l = 0; l < face2->numedges; l++) + { + edge2num = abs(aasworld.edgeindex[face2->firstedge + l]); + edge2 = &aasworld.edges[edge2num]; + //calculate the minimum distance between the two edges + v1 = aasworld.vertexes[edge1->v[0]]; + v2 = aasworld.vertexes[edge1->v[1]]; + v3 = aasworld.vertexes[edge2->v[0]]; + v4 = aasworld.vertexes[edge2->v[1]]; + //get the ground planes + plane1 = &aasworld.planes[face1->planenum]; + plane2 = &aasworld.planes[face2->planenum]; + // + bestdist = AAS_ClosestEdgePoints(v1, v2, v3, v4, plane1, plane2, + beststart, bestend, + beststart2, bestend2, bestdist); + } //end for + } //end for + } //end for + } //end for + VectorMiddle(beststart, beststart2, beststart); + VectorMiddle(bestend, bestend2, bestend); + if (bestdist > 4 && bestdist < maxjumpdistance) + { +// Log_Write("shortest distance between %d and %d is %f\r\n", area1num, area2num, bestdist); + // if very close and almost no height difference then the bot can walk + if (bestdist <= 48 && fabs(beststart[2] - bestend[2]) < 8) + { + speed = 400; + traveltype = TRAVEL_WALKOFFLEDGE; + } //end if + else if (AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) + { + //FIXME: why multiply with 1.2??? + speed *= 1.2f; + traveltype = TRAVEL_WALKOFFLEDGE; + } //end else if + else + { + //get the horizontal speed for the jump, if it isn't possible to calculate this + //speed (the jump is not possible) then there's no jump reachability created + if (!AAS_HorizontalVelocityForJump(phys_jumpvel, beststart, bestend, &speed)) + return qfalse; + speed *= 1.05f; + traveltype = TRAVEL_JUMP; + // + //NOTE: test if the horizontal distance isn't too small + VectorSubtract(bestend, beststart, dir); + dir[2] = 0; + if (VectorLength(dir) < 10) + return qfalse; + } //end if + // + VectorSubtract(bestend, beststart, dir); + VectorNormalize(dir); + VectorMA(beststart, 1, dir, teststart); + // + VectorCopy(teststart, testend); + testend[2] -= 100; + trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1); + // + if (trace.startsolid) + return qfalse; + if (trace.fraction < 1) + { + plane = &aasworld.planes[trace.planenum]; + // if the bot can stand on the surface + if (DotProduct(plane->normal, up) >= 0.7) + { + // if no lava or slime below + if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME))) + { + if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier) + return qfalse; + } //end if + } //end if + } //end if + // + VectorMA(bestend, -1, dir, teststart); + // + VectorCopy(teststart, testend); + testend[2] -= 100; + trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1); + // + if (trace.startsolid) + return qfalse; + if (trace.fraction < 1) + { + plane = &aasworld.planes[trace.planenum]; + // if the bot can stand on the surface + if (DotProduct(plane->normal, up) >= 0.7) + { + // if no lava or slime below + if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME))) + { + if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier) + return qfalse; + } //end if + } //end if + } //end if + // + // get command movement + VectorClear(cmdmove); + if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) + cmdmove[2] = aassettings.phys_jumpvel; + else + cmdmove[2] = 0; + // + VectorSubtract(bestend, beststart, dir); + dir[2] = 0; + VectorNormalize(dir); + CrossProduct(dir, up, sidewards); + // + stopevent = SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE; + if (!AAS_AreaClusterPortal(area1num) && !AAS_AreaClusterPortal(area2num)) + stopevent |= SE_TOUCHCLUSTERPORTAL; + // + for (i = 0; i < 3; i++) + { + // + if (i == 1) + VectorAdd(testend, sidewards, testend); + else if (i == 2) + VectorSubtract(bestend, sidewards, testend); + else + VectorCopy(bestend, testend); + VectorSubtract(testend, beststart, dir); + dir[2] = 0; + VectorNormalize(dir); + VectorScale(dir, speed, velocity); + // + AAS_PredictClientMovement(&move, -1, beststart, PRESENCE_NORMAL, qtrue, + velocity, cmdmove, 3, 30, 0.1f, + stopevent, 0, qfalse); + // if prediction time wasn't enough to fully predict the movement + if (move.frames >= 30) + return qfalse; + // don't enter slime or lava and don't fall from too high + if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA)) + return qfalse; + // never jump or fall through a cluster portal + if (move.stopevent & SE_TOUCHCLUSTERPORTAL) + return qfalse; + //the end position should be in area2, also test a little bit back + //because the predicted jump could have rushed through the area + VectorMA(move.endpos, -64, dir, teststart); + teststart[2] += 1; + numareas = AAS_TraceAreas(move.endpos, teststart, areas, NULL, sizeof(areas) / sizeof(int)); + for (j = 0; j < numareas; j++) + { + if (areas[j] == area2num) + break; + } //end for + if (j < numareas) + break; + } + if (i >= 3) + return qfalse; + // +#ifdef REACH_DEBUG + //create the reachability + Log_Write("jump reachability between %d and %d\r\n", area1num, area2num); +#endif //REACH_DEBUG + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = 0; + VectorCopy(beststart, lreach->start); + VectorCopy(bestend, lreach->end); + lreach->traveltype = traveltype; + + VectorSubtract(bestend, beststart, dir); + height = dir[2]; + dir[2] = 0; + if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE && height > VectorLength(dir)) + { + lreach->traveltime = aassettings.rs_startwalkoffledge + height * 50 / aassettings.phys_gravity; + } + else + { + lreach->traveltime = aassettings.rs_startjump + VectorDistance(bestend, beststart) * 240 / aassettings.phys_maxwalkvelocity; + } //end if + // + if (!AAS_AreaJumpPad(area2num)) + { + if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta5) + { + lreach->traveltime += aassettings.rs_falldamage5; + } //end if + else if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta10) + { + lreach->traveltime += aassettings.rs_falldamage10; + } //end if + } //end if + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) + reach_jump++; + else + reach_walkoffledge++; + } //end if + return qfalse; +} //end of the function AAS_Reachability_Jump +//=========================================================================== +// create a possible ladder reachability from area1 to area2 +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Reachability_Ladder(int area1num, int area2num) +{ + int i, j, k, l, edge1num, edge2num, sharededgenum = 0, lowestedgenum = 0; + int face1num, face2num, ladderface1num = 0, ladderface2num = 0; + int ladderface1vertical, ladderface2vertical, firstv; + float face1area, face2area, bestface1area = -9999, bestface2area = -9999; + float phys_jumpvel, maxjumpheight; + vec3_t area1point, area2point, v1, v2, up = {0, 0, 1}; + vec3_t mid, lowestpoint = {0, 0}, start, end, sharededgevec, dir; + aas_area_t *area1, *area2; + aas_face_t *face1, *face2, *ladderface1 = NULL, *ladderface2 = NULL; + aas_plane_t *plane1, *plane2; + aas_edge_t *sharededge, *edge1; + aas_lreachability_t *lreach; + aas_trace_t trace; + + if (!AAS_AreaLadder(area1num) || !AAS_AreaLadder(area2num)) return qfalse; + // + phys_jumpvel = aassettings.phys_jumpvel; + //maximum height a player can jump with the given initial z velocity + maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel); + + area1 = &aasworld.areas[area1num]; + area2 = &aasworld.areas[area2num]; + + for (i = 0; i < area1->numfaces; i++) + { + face1num = aasworld.faceindex[area1->firstface + i]; + face1 = &aasworld.faces[abs(face1num)]; + //if not a ladder face + if (!(face1->faceflags & FACE_LADDER)) continue; + // + for (j = 0; j < area2->numfaces; j++) + { + face2num = aasworld.faceindex[area2->firstface + j]; + face2 = &aasworld.faces[abs(face2num)]; + //if not a ladder face + if (!(face2->faceflags & FACE_LADDER)) continue; + //check if the faces share an edge + for (k = 0; k < face1->numedges; k++) + { + edge1num = aasworld.edgeindex[face1->firstedge + k]; + for (l = 0; l < face2->numedges; l++) + { + edge2num = aasworld.edgeindex[face2->firstedge + l]; + if (abs(edge1num) == abs(edge2num)) + { + //get the face with the largest area + face1area = AAS_FaceArea(face1); + face2area = AAS_FaceArea(face2); + if (face1area > bestface1area && face2area > bestface2area) + { + bestface1area = face1area; + bestface2area = face2area; + ladderface1 = face1; + ladderface2 = face2; + ladderface1num = face1num; + ladderface2num = face2num; + sharededgenum = edge1num; + } //end if + break; + } //end if + } //end for + if (l != face2->numedges) break; + } //end for + } //end for + } //end for + // + if (ladderface1 && ladderface2) + { + //get the middle of the shared edge + sharededge = &aasworld.edges[abs(sharededgenum)]; + firstv = sharededgenum < 0; + // + VectorCopy(aasworld.vertexes[sharededge->v[firstv]], v1); + VectorCopy(aasworld.vertexes[sharededge->v[!firstv]], v2); + VectorAdd(v1, v2, area1point); + VectorScale(area1point, 0.5, area1point); + VectorCopy(area1point, area2point); + // + //if the face plane in area 1 is pretty much vertical + plane1 = &aasworld.planes[ladderface1->planenum ^ (ladderface1num < 0)]; + plane2 = &aasworld.planes[ladderface2->planenum ^ (ladderface2num < 0)]; + // + //get the points really into the areas + VectorSubtract(v2, v1, sharededgevec); + CrossProduct(plane1->normal, sharededgevec, dir); + VectorNormalize(dir); + //NOTE: 32 because that's larger than 16 (bot bbox x,y) + VectorMA(area1point, -32, dir, area1point); + VectorMA(area2point, 32, dir, area2point); + // + ladderface1vertical = abs(DotProduct(plane1->normal, up)) < 0.1; + ladderface2vertical = abs(DotProduct(plane2->normal, up)) < 0.1; + //there's only reachability between vertical ladder faces + if (!ladderface1vertical && !ladderface2vertical) return qfalse; + //if both vertical ladder faces + if (ladderface1vertical && ladderface2vertical + //and the ladder faces do not make a sharp corner + && DotProduct(plane1->normal, plane2->normal) > 0.7 + //and the shared edge is not too vertical + && abs(DotProduct(sharededgevec, up)) < 0.7) + { + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = ladderface1num; + lreach->edgenum = abs(sharededgenum); + VectorCopy(area1point, lreach->start); + //VectorCopy(area2point, lreach->end); + VectorMA(area2point, -3, plane1->normal, lreach->end); + lreach->traveltype = TRAVEL_LADDER; + lreach->traveltime = 10; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + reach_ladder++; + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area1num; + lreach->facenum = ladderface2num; + lreach->edgenum = abs(sharededgenum); + VectorCopy(area2point, lreach->start); + //VectorCopy(area1point, lreach->end); + VectorMA(area1point, -3, plane1->normal, lreach->end); + lreach->traveltype = TRAVEL_LADDER; + lreach->traveltime = 10; + lreach->next = areareachability[area2num]; + areareachability[area2num] = lreach; + // + reach_ladder++; + // + return qtrue; + } //end if + //if the second ladder face is also a ground face + //create ladder end (just ladder) reachability and + //walk off a ladder (ledge) reachability + if (ladderface1vertical && (ladderface2->faceflags & FACE_GROUND)) + { + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = ladderface1num; + lreach->edgenum = abs(sharededgenum); + VectorCopy(area1point, lreach->start); + VectorCopy(area2point, lreach->end); + lreach->end[2] += 16; + VectorMA(lreach->end, -15, plane1->normal, lreach->end); + lreach->traveltype = TRAVEL_LADDER; + lreach->traveltime = 10; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + reach_ladder++; + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area1num; + lreach->facenum = ladderface2num; + lreach->edgenum = abs(sharededgenum); + VectorCopy(area2point, lreach->start); + VectorCopy(area1point, lreach->end); + lreach->traveltype = TRAVEL_WALKOFFLEDGE; + lreach->traveltime = 10; + lreach->next = areareachability[area2num]; + areareachability[area2num] = lreach; + // + reach_walkoffledge++; + // + return qtrue; + } //end if + // + if (ladderface1vertical) + { + //find lowest edge of the ladder face + lowestpoint[2] = 99999; + for (i = 0; i < ladderface1->numedges; i++) + { + edge1num = abs(aasworld.edgeindex[ladderface1->firstedge + i]); + edge1 = &aasworld.edges[edge1num]; + // + VectorCopy(aasworld.vertexes[edge1->v[0]], v1); + VectorCopy(aasworld.vertexes[edge1->v[1]], v2); + // + VectorAdd(v1, v2, mid); + VectorScale(mid, 0.5, mid); + // + if (mid[2] < lowestpoint[2]) + { + VectorCopy(mid, lowestpoint); + lowestedgenum = edge1num; + } //end if + } //end for + // + plane1 = &aasworld.planes[ladderface1->planenum]; + //trace down in the middle of this edge + VectorMA(lowestpoint, 5, plane1->normal, start); + VectorCopy(start, end); + start[2] += 5; + end[2] -= 100; + //trace without entity collision + trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); + // + // +#ifdef REACH_DEBUG + if (trace.startsolid) + { + Log_Write("trace from area %d started in solid\r\n", area1num); + } //end if +#endif //REACH_DEBUG + // + trace.endpos[2] += 1; + area2num = AAS_PointAreaNum(trace.endpos); + // + area2 = &aasworld.areas[area2num]; + for (i = 0; i < area2->numfaces; i++) + { + face2num = aasworld.faceindex[area2->firstface + i]; + face2 = &aasworld.faces[abs(face2num)]; + // + if (face2->faceflags & FACE_LADDER) + { + plane2 = &aasworld.planes[face2->planenum]; + if (abs(DotProduct(plane2->normal, up)) < 0.1) break; + } //end if + } //end for + //if from another area without vertical ladder faces + if (i >= area2->numfaces && area2num != area1num && + //the reachabilities shouldn't exist already + !AAS_ReachabilityExists(area1num, area2num) && + !AAS_ReachabilityExists(area2num, area1num)) + { + //if the height is jumpable + if (start[2] - trace.endpos[2] < maxjumpheight) + { + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = ladderface1num; + lreach->edgenum = lowestedgenum; + VectorCopy(lowestpoint, lreach->start); + VectorCopy(trace.endpos, lreach->end); + lreach->traveltype = TRAVEL_LADDER; + lreach->traveltime = 10; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + reach_ladder++; + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area1num; + lreach->facenum = ladderface1num; + lreach->edgenum = lowestedgenum; + VectorCopy(trace.endpos, lreach->start); + //get the end point a little bit into the ladder + VectorMA(lowestpoint, -5, plane1->normal, lreach->end); + //get the end point a little higher + lreach->end[2] += 10; + lreach->traveltype = TRAVEL_JUMP; + lreach->traveltime = 10; + lreach->next = areareachability[area2num]; + areareachability[area2num] = lreach; + // + reach_jump++; + // + return qtrue; +#ifdef REACH_DEBUG + Log_Write("jump up to ladder reach between %d and %d\r\n", area2num, area1num); +#endif //REACH_DEBUG + } //end if +#ifdef REACH_DEBUG + else Log_Write("jump too high between area %d and %d\r\n", area2num, area1num); +#endif //REACH_DEBUG + } //end if + /*//if slime or lava below the ladder + //try jump reachability from far towards the ladder + if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME + | AREACONTENTS_LAVA)) + { + for (i = 20; i <= 120; i += 20) + { + //trace down in the middle of this edge + VectorMA(lowestpoint, i, plane1->normal, start); + VectorCopy(start, end); + start[2] += 5; + end[2] -= 100; + //trace without entity collision + trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); + // + if (trace.startsolid) break; + trace.endpos[2] += 1; + area2num = AAS_PointAreaNum(trace.endpos); + if (area2num == area1num) continue; + // + if (start[2] - trace.endpos[2] > maxjumpheight) continue; + if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME + | AREACONTENTS_LAVA)) continue; + // + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area1num; + lreach->facenum = ladderface1num; + lreach->edgenum = lowestedgenum; + VectorCopy(trace.endpos, lreach->start); + VectorCopy(lowestpoint, lreach->end); + lreach->end[2] += 5; + lreach->traveltype = TRAVEL_JUMP; + lreach->traveltime = 10; + lreach->next = areareachability[area2num]; + areareachability[area2num] = lreach; + // + reach_jump++; + // + Log_Write("jump far to ladder reach between %d and %d\r\n", area2num, area1num); + // + break; + } //end for + } //end if*/ + } //end if + } //end if + return qfalse; +} //end of the function AAS_Reachability_Ladder +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_TravelFlagsForTeam(int ent) +{ + int notteam; + + if (!AAS_IntForBSPEpairKey(ent, "bot_notteam", ¬team)) + return 0; + if (notteam == 1) + return TRAVELFLAG_NOTTEAM1; + if (notteam == 2) + return TRAVELFLAG_NOTTEAM2; + return 0; +} //end of the function AAS_TravelFlagsForTeam +//=========================================================================== +// create possible teleporter reachabilities +// this is very game dependent.... :( +// +// classname = trigger_multiple or trigger_teleport +// target = "t1" +// +// classname = target_teleporter +// targetname = "t1" +// target = "t2" +// +// classname = misc_teleporter_dest +// targetname = "t2" +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Reachability_Teleport(void) +{ + int area1num, area2num; + char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY]; + char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; + int ent, dest; + float angle; + vec3_t origin, destorigin, mins, maxs, end, angles; + vec3_t mid, velocity, cmdmove; + aas_lreachability_t *lreach; + aas_clientmove_t move; + aas_trace_t trace; + aas_link_t *areas, *link; + + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + if (!strcmp(classname, "trigger_multiple")) + { + AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); +//#ifdef REACH_DEBUG + botimport.Print(PRT_MESSAGE, "trigger_multiple model = \"%s\"\n", model); +//#endif REACH_DEBUG + VectorClear(angles); + AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin); + // + if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY)) + { + botimport.Print(PRT_ERROR, "trigger_multiple at %1.0f %1.0f %1.0f without target\n", + origin[0], origin[1], origin[2]); + continue; + } //end if + for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest)) + { + if (!AAS_ValueForBSPEpairKey(dest, "classname", classname, MAX_EPAIRKEY)) continue; + if (!strcmp(classname, "target_teleporter")) + { + if (!AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY)) continue; + if (!strcmp(targetname, target)) + { + break; + } //end if + } //end if + } //end for + if (!dest) + { + continue; + } //end if + if (!AAS_ValueForBSPEpairKey(dest, "target", target, MAX_EPAIRKEY)) + { + botimport.Print(PRT_ERROR, "target_teleporter without target\n"); + continue; + } //end if + } //end else + else if (!strcmp(classname, "trigger_teleport")) + { + AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); +//#ifdef REACH_DEBUG + botimport.Print(PRT_MESSAGE, "trigger_teleport model = \"%s\"\n", model); +//#endif REACH_DEBUG + VectorClear(angles); + AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin); + // + if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY)) + { + botimport.Print(PRT_ERROR, "trigger_teleport at %1.0f %1.0f %1.0f without target\n", + origin[0], origin[1], origin[2]); + continue; + } //end if + } //end if + else + { + continue; + } //end else + // + for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest)) + { + //classname should be misc_teleporter_dest + //but I've also seen target_position and actually any + //entity could be used... burp + if (AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY)) + { + if (!strcmp(targetname, target)) + { + break; + } //end if + } //end if + } //end for + if (!dest) + { + botimport.Print(PRT_ERROR, "teleporter without misc_teleporter_dest (%s)\n", target); + continue; + } //end if + if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin)) + { + botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target); + continue; + } //end if + // + area2num = AAS_PointAreaNum(destorigin); + //if not teleported into a teleporter or into a jumppad + if (!AAS_AreaTeleporter(area2num) && !AAS_AreaJumpPad(area2num)) + { + VectorCopy(destorigin, end); + end[2] -= 64; + trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1); + if (trace.startsolid) + { + botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target); + continue; + } //end if + area2num = AAS_PointAreaNum(trace.endpos); + // + /* + if (!AAS_AreaTeleporter(area2num) && + !AAS_AreaJumpPad(area2num) && + !AAS_AreaGrounded(area2num)) + { + VectorCopy(trace.endpos, destorigin); + } + else*/ + { + //predict where you'll end up + AAS_FloatForBSPEpairKey(dest, "angle", &angle); + if (angle) + { + VectorSet(angles, 0, angle, 0); + AngleVectors(angles, velocity, NULL, NULL); + VectorScale(velocity, 400, velocity); + } //end if + else + { + VectorClear(velocity); + } //end else + VectorClear(cmdmove); + AAS_PredictClientMovement(&move, -1, destorigin, PRESENCE_NORMAL, qfalse, + velocity, cmdmove, 0, 30, 0.1f, + SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| + SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, qfalse); //qtrue); + area2num = AAS_PointAreaNum(move.endpos); + if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA)) + { + botimport.Print(PRT_WARNING, "teleported into slime or lava at dest %s\n", target); + } //end if + VectorCopy(move.endpos, destorigin); + } //end else + } //end if + // + //botimport.Print(PRT_MESSAGE, "teleporter brush origin at %f %f %f\n", origin[0], origin[1], origin[2]); + //botimport.Print(PRT_MESSAGE, "teleporter brush mins = %f %f %f\n", mins[0], mins[1], mins[2]); + //botimport.Print(PRT_MESSAGE, "teleporter brush maxs = %f %f %f\n", maxs[0], maxs[1], maxs[2]); + VectorAdd(origin, mins, mins); + VectorAdd(origin, maxs, maxs); + // + VectorAdd(mins, maxs, mid); + VectorScale(mid, 0.5, mid); + //link an invalid (-1) entity + areas = AAS_LinkEntityClientBBox(mins, maxs, -1, PRESENCE_CROUCH); + if (!areas) botimport.Print(PRT_MESSAGE, "trigger_multiple not in any area\n"); + // + for (link = areas; link; link = link->next_area) + { + //if (!AAS_AreaGrounded(link->areanum)) continue; + if (!AAS_AreaTeleporter(link->areanum)) continue; + // + area1num = link->areanum; + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) break; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = 0; + VectorCopy(mid, lreach->start); + VectorCopy(destorigin, lreach->end); + lreach->traveltype = TRAVEL_TELEPORT; + lreach->traveltype |= AAS_TravelFlagsForTeam(ent); + lreach->traveltime = aassettings.rs_teleport; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + reach_teleport++; + } //end for + //unlink the invalid entity + AAS_UnlinkFromAreas(areas); + } //end for +} //end of the function AAS_Reachability_Teleport +//=========================================================================== +// create possible elevator (func_plat) reachabilities +// this is very game dependent.... :( +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Reachability_Elevator(void) +{ + int area1num, area2num, modelnum, i, j, k, l, n, p; + float lip, height, speed; + char model[MAX_EPAIRKEY], classname[MAX_EPAIRKEY]; + int ent; + vec3_t mins, maxs, origin, angles = {0, 0, 0}; + vec3_t pos1, pos2, mids, platbottom, plattop; + vec3_t bottomorg, toporg, start, end, dir; + vec_t xvals[8], yvals[8], xvals_top[8], yvals_top[8]; + aas_lreachability_t *lreach; + aas_trace_t trace; + +#ifdef REACH_DEBUG + Log_Write("AAS_Reachability_Elevator\r\n"); +#endif //REACH_DEBUG + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + if (!strcmp(classname, "func_plat")) + { +#ifdef REACH_DEBUG + Log_Write("found func plat\r\n"); +#endif //REACH_DEBUG + if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) + { + botimport.Print(PRT_ERROR, "func_plat without model\n"); + continue; + } //end if + //get the model number, and skip the leading * + modelnum = atoi(model+1); + if (modelnum <= 0) + { + botimport.Print(PRT_ERROR, "func_plat with invalid model number\n"); + continue; + } //end if + //get the mins, maxs and origin of the model + //NOTE: the origin is usually (0,0,0) and the mins and maxs + // are the absolute mins and maxs + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); + // + AAS_VectorForBSPEpairKey(ent, "origin", origin); + //pos1 is the top position, pos2 is the bottom + VectorCopy(origin, pos1); + VectorCopy(origin, pos2); + //get the lip of the plat + AAS_FloatForBSPEpairKey(ent, "lip", &lip); + if (!lip) lip = 8; + //get the movement height of the plat + AAS_FloatForBSPEpairKey(ent, "height", &height); + if (!height) height = (maxs[2] - mins[2]) - lip; + //get the speed of the plat + AAS_FloatForBSPEpairKey(ent, "speed", &speed); + if (!speed) speed = 200; + //get bottom position below pos1 + pos2[2] -= height; + // + //get a point just above the plat in the bottom position + VectorAdd(mins, maxs, mids); + VectorMA(pos2, 0.5, mids, platbottom); + platbottom[2] = maxs[2] - (pos1[2] - pos2[2]) + 2; + //get a point just above the plat in the top position + VectorAdd(mins, maxs, mids); + VectorMA(pos2, 0.5, mids, plattop); + plattop[2] = maxs[2] + 2; + // + /*if (!area1num) + { + Log_Write("no grounded area near plat bottom\r\n"); + continue; + } //end if*/ + //get the mins and maxs a little larger + for (i = 0; i < 3; i++) + { + mins[i] -= 1; + maxs[i] += 1; + } //end for + // + //botimport.Print(PRT_MESSAGE, "platbottom[2] = %1.1f plattop[2] = %1.1f\n", platbottom[2], plattop[2]); + // + VectorAdd(mins, maxs, mids); + VectorScale(mids, 0.5, mids); + // + xvals[0] = mins[0]; xvals[1] = mids[0]; xvals[2] = maxs[0]; xvals[3] = mids[0]; + yvals[0] = mids[1]; yvals[1] = maxs[1]; yvals[2] = mids[1]; yvals[3] = mins[1]; + // + xvals[4] = mins[0]; xvals[5] = maxs[0]; xvals[6] = maxs[0]; xvals[7] = mins[0]; + yvals[4] = maxs[1]; yvals[5] = maxs[1]; yvals[6] = mins[1]; yvals[7] = mins[1]; + //find adjacent areas around the bottom of the plat + for (i = 0; i < 9; i++) + { + if (i < 8) //check at the sides of the plat + { + bottomorg[0] = origin[0] + xvals[i]; + bottomorg[1] = origin[1] + yvals[i]; + bottomorg[2] = platbottom[2] + 16; + //get a grounded or swim area near the plat in the bottom position + area1num = AAS_PointAreaNum(bottomorg); + for (k = 0; k < 16; k++) + { + if (area1num) + { + if (AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) break; + } //end if + bottomorg[2] += 4; + area1num = AAS_PointAreaNum(bottomorg); + } //end if + //if in solid + if (k >= 16) + { + continue; + } //end if + } //end if + else //at the middle of the plat + { + VectorCopy(plattop, bottomorg); + bottomorg[2] += 24; + area1num = AAS_PointAreaNum(bottomorg); + if (!area1num) continue; + VectorCopy(platbottom, bottomorg); + bottomorg[2] += 24; + } //end else + //look at adjacent areas around the top of the plat + //make larger steps to outside the plat everytime + for (n = 0; n < 3; n++) + { + for (k = 0; k < 3; k++) + { + mins[k] -= 4; + maxs[k] += 4; + } //end for + xvals_top[0] = mins[0]; xvals_top[1] = mids[0]; xvals_top[2] = maxs[0]; xvals_top[3] = mids[0]; + yvals_top[0] = mids[1]; yvals_top[1] = maxs[1]; yvals_top[2] = mids[1]; yvals_top[3] = mins[1]; + // + xvals_top[4] = mins[0]; xvals_top[5] = maxs[0]; xvals_top[6] = maxs[0]; xvals_top[7] = mins[0]; + yvals_top[4] = maxs[1]; yvals_top[5] = maxs[1]; yvals_top[6] = mins[1]; yvals_top[7] = mins[1]; + // + for (j = 0; j < 8; j++) + { + toporg[0] = origin[0] + xvals_top[j]; + toporg[1] = origin[1] + yvals_top[j]; + toporg[2] = plattop[2] + 16; + //get a grounded or swim area near the plat in the top position + area2num = AAS_PointAreaNum(toporg); + for (l = 0; l < 16; l++) + { + if (area2num) + { + if (AAS_AreaGrounded(area2num) || AAS_AreaSwim(area2num)) + { + VectorCopy(plattop, start); + start[2] += 32; + VectorCopy(toporg, end); + end[2] += 1; + trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); + if (trace.fraction >= 1) break; + } //end if + } //end if + toporg[2] += 4; + area2num = AAS_PointAreaNum(toporg); + } //end if + //if in solid + if (l >= 16) continue; + //never create a reachability in the same area + if (area2num == area1num) continue; + //if the area isn't grounded + if (!AAS_AreaGrounded(area2num)) continue; + //if there already exists reachability between the areas + if (AAS_ReachabilityExists(area1num, area2num)) continue; + //if the reachability start is within the elevator bounding box + VectorSubtract(bottomorg, platbottom, dir); + VectorNormalize(dir); + dir[0] = bottomorg[0] + 24 * dir[0]; + dir[1] = bottomorg[1] + 24 * dir[1]; + dir[2] = bottomorg[2]; + // + for (p = 0; p < 3; p++) + if (dir[p] < origin[p] + mins[p] || dir[p] > origin[p] + maxs[p]) break; + if (p >= 3) continue; + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) continue; + lreach->areanum = area2num; + //the facenum is the model number + lreach->facenum = modelnum; + //the edgenum is the height + lreach->edgenum = (int) height; + // + VectorCopy(dir, lreach->start); + VectorCopy(toporg, lreach->end); + lreach->traveltype = TRAVEL_ELEVATOR; + lreach->traveltype |= AAS_TravelFlagsForTeam(ent); + lreach->traveltime = aassettings.rs_startelevator + height * 100 / speed; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + //don't go any further to the outside + n = 9999; + // +#ifdef REACH_DEBUG + Log_Write("elevator reach from %d to %d\r\n", area1num, area2num); +#endif //REACH_DEBUG + // + reach_elevator++; + } //end for + } //end for + } //end for + } //end if + } //end for +} //end of the function AAS_Reachability_Elevator +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_lreachability_t *AAS_FindFaceReachabilities(vec3_t *facepoints, int numpoints, aas_plane_t *plane, int towardsface) +{ + int i, j, k, l; + int facenum, edgenum, bestfacenum; + float *v1, *v2, *v3, *v4; + float bestdist, speed, hordist, dist; + vec3_t beststart, beststart2, bestend, bestend2, tmp, hordir, testpoint; + aas_lreachability_t *lreach, *lreachabilities; + aas_area_t *area; + aas_face_t *face; + aas_edge_t *edge; + aas_plane_t *faceplane, *bestfaceplane; + + // + lreachabilities = NULL; + bestfacenum = 0; + bestfaceplane = NULL; + // + for (i = 1; i < aasworld.numareas; i++) + { + area = &aasworld.areas[i]; + // get the shortest distance between one of the func_bob start edges and + // one of the face edges of area1 + bestdist = 999999; + for (j = 0; j < area->numfaces; j++) + { + facenum = aasworld.faceindex[area->firstface + j]; + face = &aasworld.faces[abs(facenum)]; + //if not a ground face + if (!(face->faceflags & FACE_GROUND)) continue; + //get the ground planes + faceplane = &aasworld.planes[face->planenum]; + // + for (k = 0; k < face->numedges; k++) + { + edgenum = abs(aasworld.edgeindex[face->firstedge + k]); + edge = &aasworld.edges[edgenum]; + //calculate the minimum distance between the two edges + v1 = aasworld.vertexes[edge->v[0]]; + v2 = aasworld.vertexes[edge->v[1]]; + // + for (l = 0; l < numpoints; l++) + { + v3 = facepoints[l]; + v4 = facepoints[(l+1) % numpoints]; + dist = AAS_ClosestEdgePoints(v1, v2, v3, v4, faceplane, plane, + beststart, bestend, + beststart2, bestend2, bestdist); + if (dist < bestdist) + { + bestfacenum = facenum; + bestfaceplane = faceplane; + bestdist = dist; + } //end if + } //end for + } //end for + } //end for + // + if (bestdist > 192) continue; + // + VectorMiddle(beststart, beststart2, beststart); + VectorMiddle(bestend, bestend2, bestend); + // + if (!towardsface) + { + VectorCopy(beststart, tmp); + VectorCopy(bestend, beststart); + VectorCopy(tmp, bestend); + } //end if + // + VectorSubtract(bestend, beststart, hordir); + hordir[2] = 0; + hordist = VectorLength(hordir); + // + if (hordist > 2 * AAS_MaxJumpDistance(aassettings.phys_jumpvel)) continue; + //the end point should not be significantly higher than the start point + if (bestend[2] - 32 > beststart[2]) continue; + //don't fall down too far + if (bestend[2] < beststart[2] - 128) continue; + //the distance should not be too far + if (hordist > 32) + { + //check for walk off ledge + if (!AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) continue; + } //end if + // + beststart[2] += 1; + bestend[2] += 1; + // + if (towardsface) VectorCopy(bestend, testpoint); + else VectorCopy(beststart, testpoint); + testpoint[2] = 0; + testpoint[2] = (bestfaceplane->dist - DotProduct(bestfaceplane->normal, testpoint)) / bestfaceplane->normal[2]; + // + if (!AAS_PointInsideFace(bestfacenum, testpoint, 0.1f)) + { + //if the faces are not overlapping then only go down + if (bestend[2] - 16 > beststart[2]) continue; + } //end if + lreach = AAS_AllocReachability(); + if (!lreach) return lreachabilities; + lreach->areanum = i; + lreach->facenum = 0; + lreach->edgenum = 0; + VectorCopy(beststart, lreach->start); + VectorCopy(bestend, lreach->end); + lreach->traveltype = 0; + lreach->traveltime = 0; + lreach->next = lreachabilities; + lreachabilities = lreach; +#ifndef BSPC + if (towardsface) AAS_PermanentLine(lreach->start, lreach->end, 1); + else AAS_PermanentLine(lreach->start, lreach->end, 2); +#endif + } //end for + return lreachabilities; +} //end of the function AAS_FindFaceReachabilities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Reachability_FuncBobbing(void) +{ + int ent, spawnflags, modelnum, axis; + int i, numareas, areas[10]; + char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; + vec3_t origin, move_end, move_start, move_start_top, move_end_top; + vec3_t mins, maxs, angles = {0, 0, 0}; + vec3_t start_edgeverts[4], end_edgeverts[4], mid; + vec3_t org, start, end, dir, points[10]; + float height; + aas_plane_t start_plane, end_plane; + aas_lreachability_t *startreach, *endreach, *nextstartreach, *nextendreach, *lreach; + aas_lreachability_t *firststartreach, *firstendreach; + + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + if (strcmp(classname, "func_bobbing")) continue; + AAS_FloatForBSPEpairKey(ent, "height", &height); + if (!height) height = 32; + // + if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) + { + botimport.Print(PRT_ERROR, "func_bobbing without model\n"); + continue; + } //end if + //get the model number, and skip the leading * + modelnum = atoi(model+1); + if (modelnum <= 0) + { + botimport.Print(PRT_ERROR, "func_bobbing with invalid model number\n"); + continue; + } //end if + //if the entity has an origin set then use it + if (!AAS_VectorForBSPEpairKey(ent, "origin", origin)) + VectorSet(origin, 0, 0, 0); + // + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL); + // + VectorAdd(mins, origin, mins); + VectorAdd(maxs, origin, maxs); + // + VectorAdd(mins, maxs, mid); + VectorScale(mid, 0.5, mid); + VectorCopy(mid, origin); + // + VectorCopy(origin, move_end); + VectorCopy(origin, move_start); + // + AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); + // set the axis of bobbing + if (spawnflags & 1) axis = 0; + else if (spawnflags & 2) axis = 1; + else axis = 2; + // + move_start[axis] -= height; + move_end[axis] += height; + // + Log_Write("funcbob model %d, start = {%1.1f, %1.1f, %1.1f} end = {%1.1f, %1.1f, %1.1f}\n", + modelnum, move_start[0], move_start[1], move_start[2], move_end[0], move_end[1], move_end[2]); + // +#ifndef BSPC + /* + AAS_DrawPermanentCross(move_start, 4, 1); + AAS_DrawPermanentCross(move_end, 4, 2); + */ +#endif + // + for (i = 0; i < 4; i++) + { + VectorCopy(move_start, start_edgeverts[i]); + start_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z + start_edgeverts[i][2] += 24; //+ player origin to ground dist + } //end for + start_edgeverts[0][0] += maxs[0] - mid[0]; + start_edgeverts[0][1] += maxs[1] - mid[1]; + start_edgeverts[1][0] += maxs[0] - mid[0]; + start_edgeverts[1][1] += mins[1] - mid[1]; + start_edgeverts[2][0] += mins[0] - mid[0]; + start_edgeverts[2][1] += mins[1] - mid[1]; + start_edgeverts[3][0] += mins[0] - mid[0]; + start_edgeverts[3][1] += maxs[1] - mid[1]; + // + start_plane.dist = start_edgeverts[0][2]; + VectorSet(start_plane.normal, 0, 0, 1); + // + for (i = 0; i < 4; i++) + { + VectorCopy(move_end, end_edgeverts[i]); + end_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z + end_edgeverts[i][2] += 24; //+ player origin to ground dist + } //end for + end_edgeverts[0][0] += maxs[0] - mid[0]; + end_edgeverts[0][1] += maxs[1] - mid[1]; + end_edgeverts[1][0] += maxs[0] - mid[0]; + end_edgeverts[1][1] += mins[1] - mid[1]; + end_edgeverts[2][0] += mins[0] - mid[0]; + end_edgeverts[2][1] += mins[1] - mid[1]; + end_edgeverts[3][0] += mins[0] - mid[0]; + end_edgeverts[3][1] += maxs[1] - mid[1]; + // + end_plane.dist = end_edgeverts[0][2]; + VectorSet(end_plane.normal, 0, 0, 1); + // +#ifndef BSPC +#if 0 + for (i = 0; i < 4; i++) + { + AAS_PermanentLine(start_edgeverts[i], start_edgeverts[(i+1)%4], 1); + AAS_PermanentLine(end_edgeverts[i], end_edgeverts[(i+1)%4], 1); + } //end for +#endif +#endif + VectorCopy(move_start, move_start_top); + move_start_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z + VectorCopy(move_end, move_end_top); + move_end_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z + // + if (!AAS_PointAreaNum(move_start_top)) continue; + if (!AAS_PointAreaNum(move_end_top)) continue; + // + for (i = 0; i < 2; i++) + { + firststartreach = firstendreach = NULL; + // + if (i == 0) + { + firststartreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qtrue); + firstendreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qfalse); + } //end if + else + { + firststartreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qtrue); + firstendreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qfalse); + } //end else + // + //create reachabilities from start to end + for (startreach = firststartreach; startreach; startreach = nextstartreach) + { + nextstartreach = startreach->next; + // + //trace = AAS_TraceClientBBox(startreach->start, move_start_top, PRESENCE_NORMAL, -1); + //if (trace.fraction < 1) continue; + // + for (endreach = firstendreach; endreach; endreach = nextendreach) + { + nextendreach = endreach->next; + // + //trace = AAS_TraceClientBBox(endreach->end, move_end_top, PRESENCE_NORMAL, -1); + //if (trace.fraction < 1) continue; + // + Log_Write("funcbob reach from area %d to %d\n", startreach->areanum, endreach->areanum); + // + // + if (i == 0) VectorCopy(move_start_top, org); + else VectorCopy(move_end_top, org); + VectorSubtract(startreach->start, org, dir); + dir[2] = 0; + VectorNormalize(dir); + VectorCopy(startreach->start, start); + VectorMA(startreach->start, 1, dir, start); + start[2] += 1; + VectorMA(startreach->start, 16, dir, end); + end[2] += 1; + // + numareas = AAS_TraceAreas(start, end, areas, points, 10); + if (numareas <= 0) continue; + if (numareas > 1) VectorCopy(points[1], startreach->start); + else VectorCopy(end, startreach->start); + // + if (!AAS_PointAreaNum(startreach->start)) continue; + if (!AAS_PointAreaNum(endreach->end)) continue; + // + lreach = AAS_AllocReachability(); + lreach->areanum = endreach->areanum; + if (i == 0) lreach->edgenum = ((int)move_start[axis] << 16) | ((int) move_end[axis] & 0x0000ffff); + else lreach->edgenum = ((int)move_end[axis] << 16) | ((int) move_start[axis] & 0x0000ffff); + lreach->facenum = (spawnflags << 16) | modelnum; + VectorCopy(startreach->start, lreach->start); + VectorCopy(endreach->end, lreach->end); +#ifndef BSPC +// AAS_DrawArrow(lreach->start, lreach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW); +// AAS_PermanentLine(lreach->start, lreach->end, 1); +#endif + lreach->traveltype = TRAVEL_FUNCBOB; + lreach->traveltype |= AAS_TravelFlagsForTeam(ent); + lreach->traveltime = aassettings.rs_funcbob; + reach_funcbob++; + lreach->next = areareachability[startreach->areanum]; + areareachability[startreach->areanum] = lreach; + // + } //end for + } //end for + for (startreach = firststartreach; startreach; startreach = nextstartreach) + { + nextstartreach = startreach->next; + AAS_FreeReachability(startreach); + } //end for + for (endreach = firstendreach; endreach; endreach = nextendreach) + { + nextendreach = endreach->next; + AAS_FreeReachability(endreach); + } //end for + //only go up with func_bobbing entities that go up and down + if (!(spawnflags & 1) && !(spawnflags & 2)) break; + } //end for + } //end for +} //end of the function AAS_Reachability_FuncBobbing +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Reachability_JumpPad(void) +{ + int face2num, i, ret, area2num, visualize, ent, bot_visualizejumppads; + //int modelnum, ent2; + //float dist, time, height, gravity, forward; + float speed, zvel, hordist; + aas_face_t *face2; + aas_area_t *area2; + aas_lreachability_t *lreach; + vec3_t areastart, facecenter, dir, cmdmove; + vec3_t velocity, absmins, absmaxs; + //vec3_t origin, ent2origin, angles, teststart; + aas_clientmove_t move; + //aas_trace_t trace; + aas_link_t *areas, *link; + //char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; + char classname[MAX_EPAIRKEY]; + +#ifdef BSPC + bot_visualizejumppads = 0; +#else + bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0"); +#endif + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + if (strcmp(classname, "trigger_push")) continue; + // + if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue; + /* + // + AAS_FloatForBSPEpairKey(ent, "speed", &speed); + if (!speed) speed = 1000; +// AAS_VectorForBSPEpairKey(ent, "angles", angles); +// AAS_SetMovedir(angles, velocity); +// VectorScale(velocity, speed, velocity); + VectorClear(angles); + //get the mins, maxs and origin of the model + AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); + if (model[0]) modelnum = atoi(model+1); + else modelnum = 0; + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin); + VectorAdd(origin, absmins, absmins); + VectorAdd(origin, absmaxs, absmaxs); + // +#ifdef REACH_DEBUG + botimport.Print(PRT_MESSAGE, "absmins = %f %f %f\n", absmins[0], absmins[1], absmins[2]); + botimport.Print(PRT_MESSAGE, "absmaxs = %f %f %f\n", absmaxs[0], absmaxs[1], absmaxs[2]); +#endif REACH_DEBUG + VectorAdd(absmins, absmaxs, origin); + VectorScale (origin, 0.5, origin); + + //get the start areas + VectorCopy(origin, teststart); + teststart[2] += 64; + trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1); + if (trace.startsolid) + { + botimport.Print(PRT_MESSAGE, "trigger_push start solid\n"); + VectorCopy(origin, areastart); + } //end if + else + { + VectorCopy(trace.endpos, areastart); + } //end else + areastart[2] += 0.125; + // + //AAS_DrawPermanentCross(origin, 4, 4); + //get the target entity + AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY); + for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2)) + { + if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue; + if (!strcmp(targetname, target)) break; + } //end for + if (!ent2) + { + botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target); + continue; + } //end if + AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin); + // + height = ent2origin[2] - origin[2]; + gravity = aassettings.sv_gravity; + time = sqrt( height / ( 0.5 * gravity ) ); + if (!time) + { + botimport.Print(PRT_MESSAGE, "trigger_push without time\n"); + continue; + } //end if + // set s.origin2 to the push velocity + VectorSubtract ( ent2origin, origin, velocity); + dist = VectorNormalize( velocity); + forward = dist / time; + //FIXME: why multiply by 1.1 + forward *= 1.1; + VectorScale(velocity, forward, velocity); + velocity[2] = time * gravity; + */ + //get the areas the jump pad brush is in + areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); + /* + for (link = areas; link; link = link->next_area) + { + if (link->areanum == 563) + { + ret = qfalse; + } + } + */ + for (link = areas; link; link = link->next_area) + { + if (AAS_AreaJumpPad(link->areanum)) break; + } //end for + if (!link) + { + botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n"); + AAS_UnlinkFromAreas(areas); + continue; + } //end if + // + botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]); + //if there is a horizontal velocity check for a reachability without air control + if (velocity[0] || velocity[1]) + { + VectorSet(cmdmove, 0, 0, 0); + //VectorCopy(velocity, cmdmove); + //cmdmove[2] = 0; + Com_Memset(&move, 0, sizeof(aas_clientmove_t)); + area2num = 0; + for (i = 0; i < 20; i++) + { + AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse, + velocity, cmdmove, 0, 30, 0.1f, + SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| + SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, bot_visualizejumppads); + area2num = move.endarea; + for (link = areas; link; link = link->next_area) + { + if (!AAS_AreaJumpPad(link->areanum)) continue; + if (link->areanum == area2num) break; + } //end if + if (!link) break; + VectorCopy(move.endpos, areastart); + VectorCopy(move.velocity, velocity); + } //end for + if (area2num && i < 20) + { + for (link = areas; link; link = link->next_area) + { + if (!AAS_AreaJumpPad(link->areanum)) continue; + if (AAS_ReachabilityExists(link->areanum, area2num)) continue; + //create a rocket or bfg jump reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) + { + AAS_UnlinkFromAreas(areas); + return; + } //end if + lreach->areanum = area2num; + //NOTE: the facenum is the Z velocity + lreach->facenum = velocity[2]; + //NOTE: the edgenum is the horizontal velocity + lreach->edgenum = sqrt(velocity[0] * velocity[0] + velocity[1] * velocity[1]); + VectorCopy(areastart, lreach->start); + VectorCopy(move.endpos, lreach->end); + lreach->traveltype = TRAVEL_JUMPPAD; + lreach->traveltype |= AAS_TravelFlagsForTeam(ent); + lreach->traveltime = aassettings.rs_jumppad; + lreach->next = areareachability[link->areanum]; + areareachability[link->areanum] = lreach; + // + reach_jumppad++; + } //end for + } //end if + } //end if + // + if (fabs(velocity[0]) > 100 || fabs(velocity[1]) > 100) continue; + //check for areas we can reach with air control + for (area2num = 1; area2num < aasworld.numareas; area2num++) + { + visualize = qfalse; + /* + if (area2num == 3568) + { + for (link = areas; link; link = link->next_area) + { + if (link->areanum == 3380) + { + visualize = qtrue; + botimport.Print(PRT_MESSAGE, "bah\n"); + } //end if + } //end for + } //end if*/ + //never try to go back to one of the original jumppad areas + //and don't create reachabilities if they already exist + for (link = areas; link; link = link->next_area) + { + if (AAS_ReachabilityExists(link->areanum, area2num)) break; + if (AAS_AreaJumpPad(link->areanum)) + { + if (link->areanum == area2num) break; + } //end if + } //end if + if (link) continue; + // + area2 = &aasworld.areas[area2num]; + for (i = 0; i < area2->numfaces; i++) + { + face2num = aasworld.faceindex[area2->firstface + i]; + face2 = &aasworld.faces[abs(face2num)]; + //if it is not a ground face + if (!(face2->faceflags & FACE_GROUND)) continue; + //get the center of the face + AAS_FaceCenter(face2num, facecenter); + //only go higher up + if (facecenter[2] < areastart[2]) continue; + //get the jumppad jump z velocity + zvel = velocity[2]; + //get the horizontal speed for the jump, if it isn't possible to calculate this + //speed + ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed); + if (ret && speed < 150) + { + //direction towards the face center + VectorSubtract(facecenter, areastart, dir); + dir[2] = 0; + hordist = VectorNormalize(dir); + //if (hordist < 1.6 * facecenter[2] - areastart[2]) + { + //get command movement + VectorScale(dir, speed, cmdmove); + // + AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse, + velocity, cmdmove, 30, 30, 0.1f, + SE_ENTERWATER|SE_ENTERSLIME| + SE_ENTERLAVA|SE_HITGROUNDDAMAGE| + SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_HITGROUNDAREA, area2num, visualize); + //if prediction time wasn't enough to fully predict the movement + //don't enter slime or lava and don't fall from too high + if (move.frames < 30 && + !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) + && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER))) + { + //never go back to the same jumppad + for (link = areas; link; link = link->next_area) + { + if (link->areanum == move.endarea) break; + } + if (!link) + { + for (link = areas; link; link = link->next_area) + { + if (!AAS_AreaJumpPad(link->areanum)) continue; + if (AAS_ReachabilityExists(link->areanum, area2num)) continue; + //create a jumppad reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) + { + AAS_UnlinkFromAreas(areas); + return; + } //end if + lreach->areanum = move.endarea; + //NOTE: the facenum is the Z velocity + lreach->facenum = velocity[2]; + //NOTE: the edgenum is the horizontal velocity + lreach->edgenum = sqrt(cmdmove[0] * cmdmove[0] + cmdmove[1] * cmdmove[1]); + VectorCopy(areastart, lreach->start); + VectorCopy(facecenter, lreach->end); + lreach->traveltype = TRAVEL_JUMPPAD; + lreach->traveltype |= AAS_TravelFlagsForTeam(ent); + lreach->traveltime = aassettings.rs_aircontrolledjumppad; + lreach->next = areareachability[link->areanum]; + areareachability[link->areanum] = lreach; + // + reach_jumppad++; + } //end for + } + } //end if + } //end if + } //end for + } //end for + } //end for + AAS_UnlinkFromAreas(areas); + } //end for +} //end of the function AAS_Reachability_JumpPad +//=========================================================================== +// never point at ground faces +// always a higher and pretty far area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Reachability_Grapple(int area1num, int area2num) +{ + int face2num, i, j, areanum, numareas, areas[20]; + float mingrappleangle, z, hordist; + bsp_trace_t bsptrace; + aas_trace_t trace; + aas_face_t *face2; + aas_area_t *area1, *area2; + aas_lreachability_t *lreach; + vec3_t areastart, facecenter, start, end, dir, down = {0, 0, -1}; + vec_t *v; + + //only grapple when on the ground or swimming + if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse; + //don't grapple from a crouch area + if (!(AAS_AreaPresenceType(area1num) & PRESENCE_NORMAL)) return qfalse; + //NOTE: disabled area swim it doesn't work right + if (AAS_AreaSwim(area1num)) return qfalse; + // + area1 = &aasworld.areas[area1num]; + area2 = &aasworld.areas[area2num]; + //don't grapple towards way lower areas + if (area2->maxs[2] < area1->mins[2]) return qfalse; + // + VectorCopy(aasworld.areas[area1num].center, start); + //if not a swim area + if (!AAS_AreaSwim(area1num)) + { + if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num, + start[0], start[1], start[2]); + VectorCopy(start, end); + end[2] -= 1000; + trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); + if (trace.startsolid) return qfalse; + VectorCopy(trace.endpos, areastart); + } //end if + else + { + if (!(AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return qfalse; + } //end else + // + //start is now the start point + // + for (i = 0; i < area2->numfaces; i++) + { + face2num = aasworld.faceindex[area2->firstface + i]; + face2 = &aasworld.faces[abs(face2num)]; + //if it is not a solid face + if (!(face2->faceflags & FACE_SOLID)) continue; + //direction towards the first vertex of the face + v = aasworld.vertexes[aasworld.edges[abs(aasworld.edgeindex[face2->firstedge])].v[0]]; + VectorSubtract(v, areastart, dir); + //if the face plane is facing away + if (DotProduct(aasworld.planes[face2->planenum].normal, dir) > 0) continue; + //get the center of the face + AAS_FaceCenter(face2num, facecenter); + //only go higher up with the grapple + if (facecenter[2] < areastart[2] + 64) continue; + //only use vertical faces or downward facing faces + if (DotProduct(aasworld.planes[face2->planenum].normal, down) < 0) continue; + //direction towards the face center + VectorSubtract(facecenter, areastart, dir); + // + z = dir[2]; + dir[2] = 0; + hordist = VectorLength(dir); + if (!hordist) continue; + //if too far + if (hordist > 2000) continue; + //check the minimal angle of the movement + mingrappleangle = 15; //15 degrees + if (z / hordist < tan(2 * M_PI * mingrappleangle / 360)) continue; + // + VectorCopy(facecenter, start); + VectorMA(facecenter, -500, aasworld.planes[face2->planenum].normal, end); + // + bsptrace = AAS_Trace(start, NULL, NULL, end, 0, CONTENTS_SOLID); + //the grapple won't stick to the sky and the grapple point should be near the AAS wall + if ((bsptrace.surface.flags & SURF_SKY) || (bsptrace.fraction * 500 > 32)) continue; + //trace a full bounding box from the area center on the ground to + //the center of the face + VectorSubtract(facecenter, areastart, dir); + VectorNormalize(dir); + VectorMA(areastart, 4, dir, start); + VectorCopy(bsptrace.endpos, end); + trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); + VectorSubtract(trace.endpos, facecenter, dir); + if (VectorLength(dir) > 24) continue; + // + VectorCopy(trace.endpos, start); + VectorCopy(trace.endpos, end); + end[2] -= AAS_FallDamageDistance(); + trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); + if (trace.fraction >= 1) continue; + //area to end in + areanum = AAS_PointAreaNum(trace.endpos); + //if not in lava or slime + if (aasworld.areasettings[areanum].contents & (AREACONTENTS_SLIME|AREACONTENTS_LAVA)) + { + continue; + } //end if + //do not go the the source area + if (areanum == area1num) continue; + //don't create reachabilities if they already exist + if (AAS_ReachabilityExists(area1num, areanum)) continue; + //only end in areas we can stand + if (!AAS_AreaGrounded(areanum)) continue; + //never go through cluster portals!! + numareas = AAS_TraceAreas(areastart, bsptrace.endpos, areas, NULL, 20); + if (numareas >= 20) continue; + for (j = 0; j < numareas; j++) + { + if (aasworld.areasettings[areas[j]].contents & AREACONTENTS_CLUSTERPORTAL) break; + } //end for + if (j < numareas) continue; + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = areanum; + lreach->facenum = face2num; + lreach->edgenum = 0; + VectorCopy(areastart, lreach->start); + //VectorCopy(facecenter, lreach->end); + VectorCopy(bsptrace.endpos, lreach->end); + lreach->traveltype = TRAVEL_GRAPPLEHOOK; + VectorSubtract(lreach->end, lreach->start, dir); + lreach->traveltime = aassettings.rs_startgrapple + VectorLength(dir) * 0.25; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + reach_grapple++; + } //end for + // + return qfalse; +} //end of the function AAS_Reachability_Grapple +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SetWeaponJumpAreaFlags(void) +{ + int ent, i; + vec3_t mins = {-15, -15, -15}, maxs = {15, 15, 15}; + vec3_t origin; + int areanum, weaponjumpareas, spawnflags; + char classname[MAX_EPAIRKEY]; + + weaponjumpareas = 0; + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + if ( + !strcmp(classname, "item_armor_body") || + !strcmp(classname, "item_armor_combat") || + !strcmp(classname, "item_health_mega") || + !strcmp(classname, "weapon_grenadelauncher") || + !strcmp(classname, "weapon_rocketlauncher") || + !strcmp(classname, "weapon_lightning") || + !strcmp(classname, "weapon_plasmagun") || + !strcmp(classname, "weapon_railgun") || + !strcmp(classname, "weapon_bfg") || + !strcmp(classname, "item_quad") || + !strcmp(classname, "item_regen") || + !strcmp(classname, "item_invulnerability")) + { + if (AAS_VectorForBSPEpairKey(ent, "origin", origin)) + { + spawnflags = 0; + AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); + //if not a stationary item + if (!(spawnflags & 1)) + { + if (!AAS_DropToFloor(origin, mins, maxs)) + { + botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n", + classname, origin[0], origin[1], origin[2]); + } //end if + } //end if + //areanum = AAS_PointAreaNum(origin); + areanum = AAS_BestReachableArea(origin, mins, maxs, origin); + //the bot may rocket jump towards this area + aasworld.areasettings[areanum].areaflags |= AREA_WEAPONJUMP; + // + //if (!AAS_AreaGrounded(areanum)) + // botimport.Print(PRT_MESSAGE, "area not grounded\n"); + // + weaponjumpareas++; + } //end if + } //end if + } //end for + for (i = 1; i < aasworld.numareas; i++) + { + if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD) + { + aasworld.areasettings[i].areaflags |= AREA_WEAPONJUMP; + weaponjumpareas++; + } //end if + } //end for + botimport.Print(PRT_MESSAGE, "%d weapon jump areas\n", weaponjumpareas); +} //end of the function AAS_SetWeaponJumpAreaFlags +//=========================================================================== +// create a possible weapon jump reachability from area1 to area2 +// +// check if there's a cool item in the second area +// check if area1 is lower than area2 +// check if the bot can rocketjump from area1 to area2 +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Reachability_WeaponJump(int area1num, int area2num) +{ + int face2num, i, n, ret, visualize; + float speed, zvel, hordist; + aas_face_t *face2; + aas_area_t *area1, *area2; + aas_lreachability_t *lreach; + vec3_t areastart, facecenter, start, end, dir, cmdmove;// teststart; + vec3_t velocity; + aas_clientmove_t move; + aas_trace_t trace; + + visualize = qfalse; +// if (area1num == 4436 && area2num == 4318) +// { +// visualize = qtrue; +// } + if (!AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) return qfalse; + if (!AAS_AreaGrounded(area2num)) return qfalse; + //NOTE: only weapon jump towards areas with an interesting item in it?? + if (!(aasworld.areasettings[area2num].areaflags & AREA_WEAPONJUMP)) return qfalse; + // + area1 = &aasworld.areas[area1num]; + area2 = &aasworld.areas[area2num]; + //don't weapon jump towards way lower areas + if (area2->maxs[2] < area1->mins[2]) return qfalse; + // + VectorCopy(aasworld.areas[area1num].center, start); + //if not a swim area + if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num, + start[0], start[1], start[2]); + VectorCopy(start, end); + end[2] -= 1000; + trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); + if (trace.startsolid) return qfalse; + VectorCopy(trace.endpos, areastart); + // + //areastart is now the start point + // + for (i = 0; i < area2->numfaces; i++) + { + face2num = aasworld.faceindex[area2->firstface + i]; + face2 = &aasworld.faces[abs(face2num)]; + //if it is not a solid face + if (!(face2->faceflags & FACE_GROUND)) continue; + //get the center of the face + AAS_FaceCenter(face2num, facecenter); + //only go higher up with weapon jumps + if (facecenter[2] < areastart[2] + 64) continue; + //NOTE: set to 2 to allow bfg jump reachabilities + for (n = 0; n < 1/*2*/; n++) + { + //get the rocket jump z velocity + if (n) zvel = AAS_BFGJumpZVelocity(areastart); + else zvel = AAS_RocketJumpZVelocity(areastart); + //get the horizontal speed for the jump, if it isn't possible to calculate this + //speed (the jump is not possible) then there's no jump reachability created + ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed); + if (ret && speed < 300) + { + //direction towards the face center + VectorSubtract(facecenter, areastart, dir); + dir[2] = 0; + hordist = VectorNormalize(dir); + //if (hordist < 1.6 * (facecenter[2] - areastart[2])) + { + //get command movement + VectorScale(dir, speed, cmdmove); + VectorSet(velocity, 0, 0, zvel); + /* + //get command movement + VectorScale(dir, speed, velocity); + velocity[2] = zvel; + VectorSet(cmdmove, 0, 0, 0); + */ + // + AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qtrue, + velocity, cmdmove, 30, 30, 0.1f, + SE_ENTERWATER|SE_ENTERSLIME| + SE_ENTERLAVA|SE_HITGROUNDDAMAGE| + SE_TOUCHJUMPPAD|SE_HITGROUND|SE_HITGROUNDAREA, area2num, visualize); + //if prediction time wasn't enough to fully predict the movement + //don't enter slime or lava and don't fall from too high + if (move.frames < 30 && + !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) + && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD))) + { + //create a rocket or bfg jump reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = 0; + VectorCopy(areastart, lreach->start); + VectorCopy(facecenter, lreach->end); + if (n) + { + lreach->traveltype = TRAVEL_BFGJUMP; + lreach->traveltime = aassettings.rs_bfgjump; + } //end if + else + { + lreach->traveltype = TRAVEL_ROCKETJUMP; + lreach->traveltime = aassettings.rs_rocketjump; + } //end else + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + reach_rocketjump++; + return qtrue; + } //end if + } //end if + } //end if + } //end for + } //end for + // + return qfalse; +} //end of the function AAS_Reachability_WeaponJump +//=========================================================================== +// calculates additional walk off ledge reachabilities for the given area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Reachability_WalkOffLedge(int areanum) +{ + int i, j, k, l, m, n, p, areas[10], numareas; + int face1num, face2num, face3num, edge1num, edge2num, edge3num; + int otherareanum, gap, reachareanum, side; + aas_area_t *area, *area2; + aas_face_t *face1, *face2, *face3; + aas_edge_t *edge; + aas_plane_t *plane; + vec_t *v1, *v2; + vec3_t sharededgevec, mid, dir, testend; + aas_lreachability_t *lreach; + aas_trace_t trace; + + if (!AAS_AreaGrounded(areanum) || AAS_AreaSwim(areanum)) return; + // + area = &aasworld.areas[areanum]; + // + for (i = 0; i < area->numfaces; i++) + { + face1num = aasworld.faceindex[area->firstface + i]; + face1 = &aasworld.faces[abs(face1num)]; + //face 1 must be a ground face + if (!(face1->faceflags & FACE_GROUND)) continue; + //go through all the edges of this ground face + for (k = 0; k < face1->numedges; k++) + { + edge1num = aasworld.edgeindex[face1->firstedge + k]; + //find another not ground face using this same edge + for (j = 0; j < area->numfaces; j++) + { + face2num = aasworld.faceindex[area->firstface + j]; + face2 = &aasworld.faces[abs(face2num)]; + //face 2 may not be a ground face + if (face2->faceflags & FACE_GROUND) continue; + //compare all the edges + for (l = 0; l < face2->numedges; l++) + { + edge2num = aasworld.edgeindex[face2->firstedge + l]; + if (abs(edge1num) == abs(edge2num)) + { + //get the area at the other side of the face + if (face2->frontarea == areanum) otherareanum = face2->backarea; + else otherareanum = face2->frontarea; + // + area2 = &aasworld.areas[otherareanum]; + //if the other area is grounded! + if (aasworld.areasettings[otherareanum].areaflags & AREA_GROUNDED) + { + //check for a possible gap + gap = qfalse; + for (n = 0; n < area2->numfaces; n++) + { + face3num = aasworld.faceindex[area2->firstface + n]; + //may not be the shared face of the two areas + if (abs(face3num) == abs(face2num)) continue; + // + face3 = &aasworld.faces[abs(face3num)]; + //find an edge shared by all three faces + for (m = 0; m < face3->numedges; m++) + { + edge3num = aasworld.edgeindex[face3->firstedge + m]; + //but the edge should be shared by all three faces + if (abs(edge3num) == abs(edge1num)) + { + if (!(face3->faceflags & FACE_SOLID)) + { + gap = qtrue; + break; + } //end if + // + if (face3->faceflags & FACE_GROUND) + { + gap = qfalse; + break; + } //end if + //FIXME: there are more situations to be handled + gap = qtrue; + break; + } //end if + } //end for + if (m < face3->numedges) break; + } //end for + if (!gap) break; + } //end if + //check for a walk off ledge reachability + edge = &aasworld.edges[abs(edge1num)]; + side = edge1num < 0; + // + v1 = aasworld.vertexes[edge->v[side]]; + v2 = aasworld.vertexes[edge->v[!side]]; + // + plane = &aasworld.planes[face1->planenum]; + //get the points really into the areas + VectorSubtract(v2, v1, sharededgevec); + CrossProduct(plane->normal, sharededgevec, dir); + VectorNormalize(dir); + // + VectorAdd(v1, v2, mid); + VectorScale(mid, 0.5, mid); + VectorMA(mid, 8, dir, mid); + // + VectorCopy(mid, testend); + testend[2] -= 1000; + trace = AAS_TraceClientBBox(mid, testend, PRESENCE_CROUCH, -1); + // + if (trace.startsolid) + { + //Log_Write("area %d: trace.startsolid\r\n", areanum); + break; + } //end if + reachareanum = AAS_PointAreaNum(trace.endpos); + if (reachareanum == areanum) + { + //Log_Write("area %d: same area\r\n", areanum); + break; + } //end if + if (AAS_ReachabilityExists(areanum, reachareanum)) + { + //Log_Write("area %d: reachability already exists\r\n", areanum); + break; + } //end if + if (!AAS_AreaGrounded(reachareanum) && !AAS_AreaSwim(reachareanum)) + { + //Log_Write("area %d, reach area %d: not grounded and not swim\r\n", areanum, reachareanum); + break; + } //end if + // + if (aasworld.areasettings[reachareanum].contents & (AREACONTENTS_SLIME + | AREACONTENTS_LAVA)) + { + //Log_Write("area %d, reach area %d: lava or slime\r\n", areanum, reachareanum); + break; + } //end if + //if not going through a cluster portal + numareas = AAS_TraceAreas(mid, testend, areas, NULL, sizeof(areas) / sizeof(int)); + for (p = 0; p < numareas; p++) + if (AAS_AreaClusterPortal(areas[p])) + break; + if (p < numareas) + break; + // if a maximum fall height is set and the bot would fall down further + if (aassettings.rs_maxfallheight && fabs(mid[2] - trace.endpos[2]) > aassettings.rs_maxfallheight) + break; + // + lreach = AAS_AllocReachability(); + if (!lreach) break; + lreach->areanum = reachareanum; + lreach->facenum = 0; + lreach->edgenum = edge1num; + VectorCopy(mid, lreach->start); + VectorCopy(trace.endpos, lreach->end); + lreach->traveltype = TRAVEL_WALKOFFLEDGE; + lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(mid[2] - trace.endpos[2]) * 50 / aassettings.phys_gravity; + if (!AAS_AreaSwim(reachareanum) && !AAS_AreaJumpPad(reachareanum)) + { + if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta5) + { + lreach->traveltime += aassettings.rs_falldamage5; + } //end if + else if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta10) + { + lreach->traveltime += aassettings.rs_falldamage10; + } //end if + } //end if + lreach->next = areareachability[areanum]; + areareachability[areanum] = lreach; + //we've got another walk off ledge reachability + reach_walkoffledge++; + } //end if + } //end for + } //end for + } //end for + } //end for +} //end of the function AAS_Reachability_WalkOffLedge +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_StoreReachability(void) +{ + int i; + aas_areasettings_t *areasettings; + aas_lreachability_t *lreach; + aas_reachability_t *reach; + + if (aasworld.reachability) FreeMemory(aasworld.reachability); + aasworld.reachability = (aas_reachability_t *) GetClearedMemory((numlreachabilities + 10) * sizeof(aas_reachability_t)); + aasworld.reachabilitysize = 1; + for (i = 0; i < aasworld.numareas; i++) + { + areasettings = &aasworld.areasettings[i]; + areasettings->firstreachablearea = aasworld.reachabilitysize; + areasettings->numreachableareas = 0; + for (lreach = areareachability[i]; lreach; lreach = lreach->next) + { + reach = &aasworld.reachability[areasettings->firstreachablearea + + areasettings->numreachableareas]; + reach->areanum = lreach->areanum; + reach->facenum = lreach->facenum; + reach->edgenum = lreach->edgenum; + VectorCopy(lreach->start, reach->start); + VectorCopy(lreach->end, reach->end); + reach->traveltype = lreach->traveltype; + reach->traveltime = lreach->traveltime; + // + areasettings->numreachableareas++; + } //end for + aasworld.reachabilitysize += areasettings->numreachableareas; + } //end for +} //end of the function AAS_StoreReachability +//=========================================================================== +// +// TRAVEL_WALK 100% equal floor height + steps +// TRAVEL_CROUCH 100% +// TRAVEL_BARRIERJUMP 100% +// TRAVEL_JUMP 80% +// TRAVEL_LADDER 100% + fall down from ladder + jump up to ladder +// TRAVEL_WALKOFFLEDGE 90% walk off very steep walls? +// TRAVEL_SWIM 100% +// TRAVEL_WATERJUMP 100% +// TRAVEL_TELEPORT 100% +// TRAVEL_ELEVATOR 100% +// TRAVEL_GRAPPLEHOOK 100% +// TRAVEL_DOUBLEJUMP 0% +// TRAVEL_RAMPJUMP 0% +// TRAVEL_STRAFEJUMP 0% +// TRAVEL_ROCKETJUMP 100% (currently limited towards areas with items) +// TRAVEL_BFGJUMP 0% (currently disabled) +// TRAVEL_JUMPPAD 100% +// TRAVEL_FUNCBOB 100% +// +// Parameter: - +// Returns: true if NOT finished +// Changes Globals: - +//=========================================================================== +int AAS_ContinueInitReachability(float time) +{ + int i, j, todo, start_time; + static float framereachability, reachability_delay; + static int lastpercentage; + + if (!aasworld.loaded) return qfalse; + //if reachability is calculated for all areas + if (aasworld.numreachabilityareas >= aasworld.numareas + 2) return qfalse; + //if starting with area 1 (area 0 is a dummy) + if (aasworld.numreachabilityareas == 1) + { + botimport.Print(PRT_MESSAGE, "calculating reachability...\n"); + lastpercentage = 0; + framereachability = 2000; + reachability_delay = 1000; + } //end if + //number of areas to calculate reachability for this cycle + todo = aasworld.numreachabilityareas + (int) framereachability; + start_time = Sys_MilliSeconds(); + //loop over the areas + for (i = aasworld.numreachabilityareas; i < aasworld.numareas && i < todo; i++) + { + aasworld.numreachabilityareas++; + //only create jumppad reachabilities from jumppad areas + if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD) + { + continue; + } //end if + //loop over the areas + for (j = 1; j < aasworld.numareas; j++) + { + if (i == j) continue; + //never create reachabilities from teleporter or jumppad areas to regular areas + if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD)) + { + if (!(aasworld.areasettings[j].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD))) + { + continue; + } //end if + } //end if + //if there already is a reachability link from area i to j + if (AAS_ReachabilityExists(i, j)) continue; + //check for a swim reachability + if (AAS_Reachability_Swim(i, j)) continue; + //check for a simple walk on equal floor height reachability + if (AAS_Reachability_EqualFloorHeight(i, j)) continue; + //check for step, barrier, waterjump and walk off ledge reachabilities + if (AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(i, j)) continue; + //check for ladder reachabilities + if (AAS_Reachability_Ladder(i, j)) continue; + //check for a jump reachability + if (AAS_Reachability_Jump(i, j)) continue; + } //end for + //never create these reachabilities from teleporter or jumppad areas + if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD)) + { + continue; + } //end if + //loop over the areas + for (j = 1; j < aasworld.numareas; j++) + { + if (i == j) continue; + // + if (AAS_ReachabilityExists(i, j)) continue; + //check for a grapple hook reachability + if (calcgrapplereach) AAS_Reachability_Grapple(i, j); + //check for a weapon jump reachability + AAS_Reachability_WeaponJump(i, j); + } //end for + //if the calculation took more time than the max reachability delay + if (Sys_MilliSeconds() - start_time > (int) reachability_delay) break; + // + if (aasworld.numreachabilityareas * 1000 / aasworld.numareas > lastpercentage) break; + } //end for + // + if (aasworld.numreachabilityareas == aasworld.numareas) + { + botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) 100.0); + botimport.Print(PRT_MESSAGE, "\nplease wait while storing reachability...\n"); + aasworld.numreachabilityareas++; + } //end if + //if this is the last step in the reachability calculations + else if (aasworld.numreachabilityareas == aasworld.numareas + 1) + { + //create additional walk off ledge reachabilities for every area + for (i = 1; i < aasworld.numareas; i++) + { + //only create jumppad reachabilities from jumppad areas + if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD) + { + continue; + } //end if + AAS_Reachability_WalkOffLedge(i); + } //end for + //create jump pad reachabilities + AAS_Reachability_JumpPad(); + //create teleporter reachabilities + AAS_Reachability_Teleport(); + //create elevator (func_plat) reachabilities + AAS_Reachability_Elevator(); + //create func_bobbing reachabilities + AAS_Reachability_FuncBobbing(); + // +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "%6d reach swim\n", reach_swim); + botimport.Print(PRT_MESSAGE, "%6d reach equal floor\n", reach_equalfloor); + botimport.Print(PRT_MESSAGE, "%6d reach step\n", reach_step); + botimport.Print(PRT_MESSAGE, "%6d reach barrier\n", reach_barrier); + botimport.Print(PRT_MESSAGE, "%6d reach waterjump\n", reach_waterjump); + botimport.Print(PRT_MESSAGE, "%6d reach walkoffledge\n", reach_walkoffledge); + botimport.Print(PRT_MESSAGE, "%6d reach jump\n", reach_jump); + botimport.Print(PRT_MESSAGE, "%6d reach ladder\n", reach_ladder); + botimport.Print(PRT_MESSAGE, "%6d reach walk\n", reach_walk); + botimport.Print(PRT_MESSAGE, "%6d reach teleport\n", reach_teleport); + botimport.Print(PRT_MESSAGE, "%6d reach funcbob\n", reach_funcbob); + botimport.Print(PRT_MESSAGE, "%6d reach elevator\n", reach_elevator); + botimport.Print(PRT_MESSAGE, "%6d reach grapple\n", reach_grapple); + botimport.Print(PRT_MESSAGE, "%6d reach rocketjump\n", reach_rocketjump); + botimport.Print(PRT_MESSAGE, "%6d reach jumppad\n", reach_jumppad); +#endif + //*/ + //store all the reachabilities + AAS_StoreReachability(); + //free the reachability link heap + AAS_ShutDownReachabilityHeap(); + // + FreeMemory(areareachability); + // + aasworld.numreachabilityareas++; + // + botimport.Print(PRT_MESSAGE, "calculating clusters...\n"); + } //end if + else + { + lastpercentage = aasworld.numreachabilityareas * 1000 / aasworld.numareas; + botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) lastpercentage / 10); + } //end else + //not yet finished + return qtrue; +} //end of the function AAS_ContinueInitReachability +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitReachability(void) +{ + if (!aasworld.loaded) return; + + if (aasworld.reachabilitysize) + { +#ifndef BSPC + if (!((int)LibVarGetValue("forcereachability"))) + { + aasworld.numreachabilityareas = aasworld.numareas + 2; + return; + } //end if +#else + aasworld.numreachabilityareas = aasworld.numareas + 2; + return; +#endif //BSPC + } //end if +#ifndef BSPC + calcgrapplereach = LibVarGetValue("grapplereach"); +#endif + aasworld.savefile = qtrue; + //start with area 1 because area zero is a dummy + aasworld.numreachabilityareas = 1; + ////aasworld.numreachabilityareas = aasworld.numareas + 1; //only calculate entity reachabilities + //setup the heap with reachability links + AAS_SetupReachabilityHeap(); + //allocate area reachability link array + areareachability = (aas_lreachability_t **) GetClearedMemory( + aasworld.numareas * sizeof(aas_lreachability_t *)); + // + AAS_SetWeaponJumpAreaFlags(); +} //end of the function AAS_InitReachable diff --git a/reaction/engine/code/botlib/be_aas_reach.h b/reaction/engine/code/botlib/be_aas_reach.h new file mode 100644 index 00000000..0ab8740b --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_reach.h @@ -0,0 +1,68 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_reach.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_reach.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +//initialize calculating the reachabilities +void AAS_InitReachability(void); +//continue calculating the reachabilities +int AAS_ContinueInitReachability(float time); +// +int AAS_BestReachableLinkArea(aas_link_t *areas); +#endif //AASINTERN + +//returns true if the are has reachabilities to other areas +int AAS_AreaReachability(int areanum); +//returns the best reachable area and goal origin for a bounding box at the given origin +int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin); +//returns the best jumppad area from which the bbox at origin is reachable +int AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs); +//returns the next reachability using the given model +int AAS_NextModelReachability(int num, int modelnum); +//returns the total area of the ground faces of the given area +float AAS_AreaGroundFaceArea(int areanum); +//returns true if the area is crouch only +int AAS_AreaCrouch(int areanum); +//returns true if a player can swim in this area +int AAS_AreaSwim(int areanum); +//returns true if the area is filled with a liquid +int AAS_AreaLiquid(int areanum); +//returns true if the area contains lava +int AAS_AreaLava(int areanum); +//returns true if the area contains slime +int AAS_AreaSlime(int areanum); +//returns true if the area has one or more ground faces +int AAS_AreaGrounded(int areanum); +//returns true if the area has one or more ladder faces +int AAS_AreaLadder(int areanum); +//returns true if the area is a jump pad +int AAS_AreaJumpPad(int areanum); +//returns true if the area is donotenter +int AAS_AreaDoNotEnter(int areanum); diff --git a/reaction/engine/code/botlib/be_aas_route.c b/reaction/engine/code/botlib/be_aas_route.c new file mode 100644 index 00000000..e31202b0 --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_route.c @@ -0,0 +1,2210 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_route.c + * + * desc: AAS + * + * $Archive: /MissionPack/code/botlib/be_aas_route.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_utils.h" +#include "l_memory.h" +#include "l_log.h" +#include "l_crc.h" +#include "l_libvar.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_aas_def.h" + +#define ROUTING_DEBUG + +//travel time in hundreths of a second = distance * 100 / speed +#define DISTANCEFACTOR_CROUCH 1.3f //crouch speed = 100 +#define DISTANCEFACTOR_SWIM 1 //should be 0.66, swim speed = 150 +#define DISTANCEFACTOR_WALK 0.33f //walk speed = 300 + +//cache refresh time +#define CACHE_REFRESHTIME 15.0f //15 seconds refresh time + +//maximum number of routing updates each frame +#define MAX_FRAMEROUTINGUPDATES 10 + + +/* + + area routing cache: + stores the distances within one cluster to a specific goal area + this goal area is in this same cluster and could be a cluster portal + for every cluster there's a list with routing cache for every area + in that cluster (including the portals of that cluster) + area cache stores aasworld.clusters[?].numreachabilityareas travel times + + portal routing cache: + stores the distances of all portals to a specific goal area + this goal area could be in any cluster and could also be a cluster portal + for every area (aasworld.numareas) the portal cache stores + aasworld.numportals travel times + +*/ + +#ifdef ROUTING_DEBUG +int numareacacheupdates; +int numportalcacheupdates; +#endif //ROUTING_DEBUG + +int routingcachesize; +int max_routingcachesize; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef ROUTING_DEBUG +void AAS_RoutingInfo(void) +{ + botimport.Print(PRT_MESSAGE, "%d area cache updates\n", numareacacheupdates); + botimport.Print(PRT_MESSAGE, "%d portal cache updates\n", numportalcacheupdates); + botimport.Print(PRT_MESSAGE, "%d bytes routing cache\n", routingcachesize); +} //end of the function AAS_RoutingInfo +#endif //ROUTING_DEBUG +//=========================================================================== +// returns the number of the area in the cluster +// assumes the given area is in the given cluster or a portal of the cluster +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +ID_INLINE int AAS_ClusterAreaNum(int cluster, int areanum) +{ + int side, areacluster; + + areacluster = aasworld.areasettings[areanum].cluster; + if (areacluster > 0) return aasworld.areasettings[areanum].clusterareanum; + else + { +/*#ifdef ROUTING_DEBUG + if (aasworld.portals[-areacluster].frontcluster != cluster && + aasworld.portals[-areacluster].backcluster != cluster) + { + botimport.Print(PRT_ERROR, "portal %d: does not belong to cluster %d\n" + , -areacluster, cluster); + } //end if +#endif //ROUTING_DEBUG*/ + side = aasworld.portals[-areacluster].frontcluster != cluster; + return aasworld.portals[-areacluster].clusterareanum[side]; + } //end else +} //end of the function AAS_ClusterAreaNum +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitTravelFlagFromType(void) +{ + int i; + + for (i = 0; i < MAX_TRAVELTYPES; i++) + { + aasworld.travelflagfortype[i] = TFL_INVALID; + } //end for + aasworld.travelflagfortype[TRAVEL_INVALID] = TFL_INVALID; + aasworld.travelflagfortype[TRAVEL_WALK] = TFL_WALK; + aasworld.travelflagfortype[TRAVEL_CROUCH] = TFL_CROUCH; + aasworld.travelflagfortype[TRAVEL_BARRIERJUMP] = TFL_BARRIERJUMP; + aasworld.travelflagfortype[TRAVEL_JUMP] = TFL_JUMP; + aasworld.travelflagfortype[TRAVEL_LADDER] = TFL_LADDER; + aasworld.travelflagfortype[TRAVEL_WALKOFFLEDGE] = TFL_WALKOFFLEDGE; + aasworld.travelflagfortype[TRAVEL_SWIM] = TFL_SWIM; + aasworld.travelflagfortype[TRAVEL_WATERJUMP] = TFL_WATERJUMP; + aasworld.travelflagfortype[TRAVEL_TELEPORT] = TFL_TELEPORT; + aasworld.travelflagfortype[TRAVEL_ELEVATOR] = TFL_ELEVATOR; + aasworld.travelflagfortype[TRAVEL_ROCKETJUMP] = TFL_ROCKETJUMP; + aasworld.travelflagfortype[TRAVEL_BFGJUMP] = TFL_BFGJUMP; + aasworld.travelflagfortype[TRAVEL_GRAPPLEHOOK] = TFL_GRAPPLEHOOK; + aasworld.travelflagfortype[TRAVEL_DOUBLEJUMP] = TFL_DOUBLEJUMP; + aasworld.travelflagfortype[TRAVEL_RAMPJUMP] = TFL_RAMPJUMP; + aasworld.travelflagfortype[TRAVEL_STRAFEJUMP] = TFL_STRAFEJUMP; + aasworld.travelflagfortype[TRAVEL_JUMPPAD] = TFL_JUMPPAD; + aasworld.travelflagfortype[TRAVEL_FUNCBOB] = TFL_FUNCBOB; +} //end of the function AAS_InitTravelFlagFromType +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +ID_INLINE int AAS_TravelFlagForType_inline(int traveltype) +{ + int tfl; + + tfl = 0; + if (tfl & TRAVELFLAG_NOTTEAM1) + tfl |= TFL_NOTTEAM1; + if (tfl & TRAVELFLAG_NOTTEAM2) + tfl |= TFL_NOTTEAM2; + traveltype &= TRAVELTYPE_MASK; + if (traveltype < 0 || traveltype >= MAX_TRAVELTYPES) + return TFL_INVALID; + tfl |= aasworld.travelflagfortype[traveltype]; + return tfl; +} //end of the function AAS_TravelFlagForType_inline +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_TravelFlagForType(int traveltype) +{ + return AAS_TravelFlagForType_inline(traveltype); +} //end of the function AAS_TravelFlagForType_inline +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_UnlinkCache(aas_routingcache_t *cache) +{ + if (cache->time_next) cache->time_next->time_prev = cache->time_prev; + else aasworld.newestcache = cache->time_prev; + if (cache->time_prev) cache->time_prev->time_next = cache->time_next; + else aasworld.oldestcache = cache->time_next; + cache->time_next = NULL; + cache->time_prev = NULL; +} //end of the function AAS_UnlinkCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_LinkCache(aas_routingcache_t *cache) +{ + if (aasworld.newestcache) + { + aasworld.newestcache->time_next = cache; + cache->time_prev = aasworld.newestcache; + } //end if + else + { + aasworld.oldestcache = cache; + cache->time_prev = NULL; + } //end else + cache->time_next = NULL; + aasworld.newestcache = cache; +} //end of the function AAS_LinkCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeRoutingCache(aas_routingcache_t *cache) +{ + AAS_UnlinkCache(cache); + routingcachesize -= cache->size; + FreeMemory(cache); +} //end of the function AAS_FreeRoutingCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveRoutingCacheInCluster( int clusternum ) +{ + int i; + aas_routingcache_t *cache, *nextcache; + aas_cluster_t *cluster; + + if (!aasworld.clusterareacache) + return; + cluster = &aasworld.clusters[clusternum]; + for (i = 0; i < cluster->numareas; i++) + { + for (cache = aasworld.clusterareacache[clusternum][i]; cache; cache = nextcache) + { + nextcache = cache->next; + AAS_FreeRoutingCache(cache); + } //end for + aasworld.clusterareacache[clusternum][i] = NULL; + } //end for +} //end of the function AAS_RemoveRoutingCacheInCluster +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveRoutingCacheUsingArea( int areanum ) +{ + int i, clusternum; + aas_routingcache_t *cache, *nextcache; + + clusternum = aasworld.areasettings[areanum].cluster; + if (clusternum > 0) + { + //remove all the cache in the cluster the area is in + AAS_RemoveRoutingCacheInCluster( clusternum ); + } //end if + else + { + // if this is a portal remove all cache in both the front and back cluster + AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].frontcluster ); + AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].backcluster ); + } //end else + // remove all portal cache + for (i = 0; i < aasworld.numareas; i++) + { + //refresh portal cache + for (cache = aasworld.portalcache[i]; cache; cache = nextcache) + { + nextcache = cache->next; + AAS_FreeRoutingCache(cache); + } //end for + aasworld.portalcache[i] = NULL; + } //end for +} //end of the function AAS_RemoveRoutingCacheUsingArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_EnableRoutingArea(int areanum, int enable) +{ + int flags; + + if (areanum <= 0 || areanum >= aasworld.numareas) + { + if (bot_developer) + { + botimport.Print(PRT_ERROR, "AAS_EnableRoutingArea: areanum %d out of range\n", areanum); + } //end if + return 0; + } //end if + flags = aasworld.areasettings[areanum].areaflags & AREA_DISABLED; + if (enable < 0) + return !flags; + + if (enable) + aasworld.areasettings[areanum].areaflags &= ~AREA_DISABLED; + else + aasworld.areasettings[areanum].areaflags |= AREA_DISABLED; + // if the status of the area changed + if ( (flags & AREA_DISABLED) != (aasworld.areasettings[areanum].areaflags & AREA_DISABLED) ) + { + //remove all routing cache involving this area + AAS_RemoveRoutingCacheUsingArea( areanum ); + } //end if + return !flags; +} //end of the function AAS_EnableRoutingArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +ID_INLINE float AAS_RoutingTime(void) +{ + return AAS_Time(); +} //end of the function AAS_RoutingTime +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_GetAreaContentsTravelFlags(int areanum) +{ + int contents, tfl; + + contents = aasworld.areasettings[areanum].contents; + tfl = 0; + if (contents & AREACONTENTS_WATER) + tfl |= TFL_WATER; + else if (contents & AREACONTENTS_SLIME) + tfl |= TFL_SLIME; + else if (contents & AREACONTENTS_LAVA) + tfl |= TFL_LAVA; + else + tfl |= TFL_AIR; + if (contents & AREACONTENTS_DONOTENTER) + tfl |= TFL_DONOTENTER; + if (contents & AREACONTENTS_NOTTEAM1) + tfl |= TFL_NOTTEAM1; + if (contents & AREACONTENTS_NOTTEAM2) + tfl |= TFL_NOTTEAM2; + if (aasworld.areasettings[areanum].areaflags & AREA_BRIDGE) + tfl |= TFL_BRIDGE; + return tfl; +} //end of the function AAS_GetAreaContentsTravelFlags +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +ID_INLINE int AAS_AreaContentsTravelFlags_inline(int areanum) +{ + return aasworld.areacontentstravelflags[areanum]; +} //end of the function AAS_AreaContentsTravelFlags +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaContentsTravelFlags(int areanum) +{ + return aasworld.areacontentstravelflags[areanum]; +} //end of the function AAS_AreaContentsTravelFlags +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitAreaContentsTravelFlags(void) +{ + int i; + + if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags); + aasworld.areacontentstravelflags = (int *) GetClearedMemory(aasworld.numareas * sizeof(int)); + // + for (i = 0; i < aasworld.numareas; i++) { + aasworld.areacontentstravelflags[i] = AAS_GetAreaContentsTravelFlags(i); + } +} //end of the function AAS_InitAreaContentsTravelFlags +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CreateReversedReachability(void) +{ + int i, n; + aas_reversedlink_t *revlink; + aas_reachability_t *reach; + aas_areasettings_t *settings; + char *ptr; +#ifdef DEBUG + int starttime; + + starttime = Sys_MilliSeconds(); +#endif + //free reversed links that have already been created + if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability); + //allocate memory for the reversed reachability links + ptr = (char *) GetClearedMemory(aasworld.numareas * sizeof(aas_reversedreachability_t) + + aasworld.reachabilitysize * sizeof(aas_reversedlink_t)); + // + aasworld.reversedreachability = (aas_reversedreachability_t *) ptr; + //pointer to the memory for the reversed links + ptr += aasworld.numareas * sizeof(aas_reversedreachability_t); + //check all reachabilities of all areas + for (i = 1; i < aasworld.numareas; i++) + { + //settings of the area + settings = &aasworld.areasettings[i]; + // + if (settings->numreachableareas >= 128) + botimport.Print(PRT_WARNING, "area %d has more than 128 reachabilities\n", i); + //create reversed links for the reachabilities + for (n = 0; n < settings->numreachableareas && n < 128; n++) + { + //reachability link + reach = &aasworld.reachability[settings->firstreachablearea + n]; + // + revlink = (aas_reversedlink_t *) ptr; + ptr += sizeof(aas_reversedlink_t); + // + revlink->areanum = i; + revlink->linknum = settings->firstreachablearea + n; + revlink->next = aasworld.reversedreachability[reach->areanum].first; + aasworld.reversedreachability[reach->areanum].first = revlink; + aasworld.reversedreachability[reach->areanum].numlinks++; + } //end for + } //end for +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "reversed reachability %d msec\n", Sys_MilliSeconds() - starttime); +#endif +} //end of the function AAS_CreateReversedReachability +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end) +{ + int intdist; + float dist; + vec3_t dir; + + VectorSubtract(start, end, dir); + dist = VectorLength(dir); + //if crouch only area + if (AAS_AreaCrouch(areanum)) dist *= DISTANCEFACTOR_CROUCH; + //if swim area + else if (AAS_AreaSwim(areanum)) dist *= DISTANCEFACTOR_SWIM; + //normal walk area + else dist *= DISTANCEFACTOR_WALK; + // + intdist = (int) dist; + //make sure the distance isn't zero + if (intdist <= 0) intdist = 1; + return intdist; +} //end of the function AAS_AreaTravelTime +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CalculateAreaTravelTimes(void) +{ + int i, l, n, size; + char *ptr; + vec3_t end; + aas_reversedreachability_t *revreach; + aas_reversedlink_t *revlink; + aas_reachability_t *reach; + aas_areasettings_t *settings; + int starttime; + + starttime = Sys_MilliSeconds(); + //if there are still area travel times, free the memory + if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes); + //get the total size of all the area travel times + size = aasworld.numareas * sizeof(unsigned short **); + for (i = 0; i < aasworld.numareas; i++) + { + revreach = &aasworld.reversedreachability[i]; + //settings of the area + settings = &aasworld.areasettings[i]; + // + size += settings->numreachableareas * sizeof(unsigned short *); + // + size += settings->numreachableareas * + PAD(revreach->numlinks, sizeof(long)) * sizeof(unsigned short); + } //end for + //allocate memory for the area travel times + ptr = (char *) GetClearedMemory(size); + aasworld.areatraveltimes = (unsigned short ***) ptr; + ptr += aasworld.numareas * sizeof(unsigned short **); + //calcluate the travel times for all the areas + for (i = 0; i < aasworld.numareas; i++) + { + //reversed reachabilities of this area + revreach = &aasworld.reversedreachability[i]; + //settings of the area + settings = &aasworld.areasettings[i]; + // + aasworld.areatraveltimes[i] = (unsigned short **) ptr; + ptr += settings->numreachableareas * sizeof(unsigned short *); + // + for (l = 0; l < settings->numreachableareas; l++) + { + aasworld.areatraveltimes[i][l] = (unsigned short *) ptr; + ptr += PAD(revreach->numlinks, sizeof(long)) * sizeof(unsigned short); + //reachability link + reach = &aasworld.reachability[settings->firstreachablearea + l]; + // + for (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++) + { + VectorCopy(aasworld.reachability[revlink->linknum].end, end); + // + aasworld.areatraveltimes[i][l][n] = AAS_AreaTravelTime(i, end, reach->start); + } //end for + } //end for + } //end for +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "area travel times %d msec\n", Sys_MilliSeconds() - starttime); +#endif +} //end of the function AAS_CalculateAreaTravelTimes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PortalMaxTravelTime(int portalnum) +{ + int l, n, t, maxt; + aas_portal_t *portal; + aas_reversedreachability_t *revreach; + aas_reversedlink_t *revlink; + aas_areasettings_t *settings; + + portal = &aasworld.portals[portalnum]; + //reversed reachabilities of this portal area + revreach = &aasworld.reversedreachability[portal->areanum]; + //settings of the portal area + settings = &aasworld.areasettings[portal->areanum]; + // + maxt = 0; + for (l = 0; l < settings->numreachableareas; l++) + { + for (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++) + { + t = aasworld.areatraveltimes[portal->areanum][l][n]; + if (t > maxt) + { + maxt = t; + } //end if + } //end for + } //end for + return maxt; +} //end of the function AAS_PortalMaxTravelTime +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitPortalMaxTravelTimes(void) +{ + int i; + + if (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes); + + aasworld.portalmaxtraveltimes = (int *) GetClearedMemory(aasworld.numportals * sizeof(int)); + + for (i = 0; i < aasworld.numportals; i++) + { + aasworld.portalmaxtraveltimes[i] = AAS_PortalMaxTravelTime(i); + //botimport.Print(PRT_MESSAGE, "portal %d max tt = %d\n", i, aasworld.portalmaxtraveltimes[i]); + } //end for +} //end of the function AAS_InitPortalMaxTravelTimes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +int AAS_FreeOldestCache(void) +{ + int i, j, bestcluster, bestarea, freed; + float besttime; + aas_routingcache_t *cache, *bestcache; + + freed = qfalse; + besttime = 999999999; + bestcache = NULL; + bestcluster = 0; + bestarea = 0; + //refresh cluster cache + for (i = 0; i < aasworld.numclusters; i++) + { + for (j = 0; j < aasworld.clusters[i].numareas; j++) + { + for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next) + { + //never remove cache leading towards a portal + if (aasworld.areasettings[cache->areanum].cluster < 0) continue; + //if this cache is older than the cache we found so far + if (cache->time < besttime) + { + bestcache = cache; + bestcluster = i; + bestarea = j; + besttime = cache->time; + } //end if + } //end for + } //end for + } //end for + if (bestcache) + { + cache = bestcache; + if (cache->prev) cache->prev->next = cache->next; + else aasworld.clusterareacache[bestcluster][bestarea] = cache->next; + if (cache->next) cache->next->prev = cache->prev; + AAS_FreeRoutingCache(cache); + freed = qtrue; + } //end if + besttime = 999999999; + bestcache = NULL; + bestarea = 0; + for (i = 0; i < aasworld.numareas; i++) + { + //refresh portal cache + for (cache = aasworld.portalcache[i]; cache; cache = cache->next) + { + if (cache->time < besttime) + { + bestcache = cache; + bestarea = i; + besttime = cache->time; + } //end if + } //end for + } //end for + if (bestcache) + { + cache = bestcache; + if (cache->prev) cache->prev->next = cache->next; + else aasworld.portalcache[bestarea] = cache->next; + if (cache->next) cache->next->prev = cache->prev; + AAS_FreeRoutingCache(cache); + freed = qtrue; + } //end if + return freed; +} //end of the function AAS_FreeOldestCache +*/ +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_FreeOldestCache(void) +{ + int clusterareanum; + aas_routingcache_t *cache; + + for (cache = aasworld.oldestcache; cache; cache = cache->time_next) { + // never free area cache leading towards a portal + if (cache->type == CACHETYPE_AREA && aasworld.areasettings[cache->areanum].cluster < 0) { + continue; + } + break; + } + if (cache) { + // unlink the cache + if (cache->type == CACHETYPE_AREA) { + //number of the area in the cluster + clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum); + // unlink from cluster area cache + if (cache->prev) cache->prev->next = cache->next; + else aasworld.clusterareacache[cache->cluster][clusterareanum] = cache->next; + if (cache->next) cache->next->prev = cache->prev; + } + else { + // unlink from portal cache + if (cache->prev) cache->prev->next = cache->next; + else aasworld.portalcache[cache->areanum] = cache->next; + if (cache->next) cache->next->prev = cache->prev; + } + AAS_FreeRoutingCache(cache); + return qtrue; + } + return qfalse; +} //end of the function AAS_FreeOldestCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_routingcache_t *AAS_AllocRoutingCache(int numtraveltimes) +{ + aas_routingcache_t *cache; + int size; + + // + size = sizeof(aas_routingcache_t) + + numtraveltimes * sizeof(unsigned short int) + + numtraveltimes * sizeof(unsigned char); + // + routingcachesize += size; + // + cache = (aas_routingcache_t *) GetClearedMemory(size); + cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) + + numtraveltimes * sizeof(unsigned short int); + cache->size = size; + return cache; +} //end of the function AAS_AllocRoutingCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeAllClusterAreaCache(void) +{ + int i, j; + aas_routingcache_t *cache, *nextcache; + aas_cluster_t *cluster; + + //free all cluster cache if existing + if (!aasworld.clusterareacache) return; + //free caches + for (i = 0; i < aasworld.numclusters; i++) + { + cluster = &aasworld.clusters[i]; + for (j = 0; j < cluster->numareas; j++) + { + for (cache = aasworld.clusterareacache[i][j]; cache; cache = nextcache) + { + nextcache = cache->next; + AAS_FreeRoutingCache(cache); + } //end for + aasworld.clusterareacache[i][j] = NULL; + } //end for + } //end for + //free the cluster cache array + FreeMemory(aasworld.clusterareacache); + aasworld.clusterareacache = NULL; +} //end of the function AAS_FreeAllClusterAreaCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitClusterAreaCache(void) +{ + int i, size; + char *ptr; + + // + for (size = 0, i = 0; i < aasworld.numclusters; i++) + { + size += aasworld.clusters[i].numareas; + } //end for + //two dimensional array with pointers for every cluster to routing cache + //for every area in that cluster + ptr = (char *) GetClearedMemory( + aasworld.numclusters * sizeof(aas_routingcache_t **) + + size * sizeof(aas_routingcache_t *)); + aasworld.clusterareacache = (aas_routingcache_t ***) ptr; + ptr += aasworld.numclusters * sizeof(aas_routingcache_t **); + for (i = 0; i < aasworld.numclusters; i++) + { + aasworld.clusterareacache[i] = (aas_routingcache_t **) ptr; + ptr += aasworld.clusters[i].numareas * sizeof(aas_routingcache_t *); + } //end for +} //end of the function AAS_InitClusterAreaCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeAllPortalCache(void) +{ + int i; + aas_routingcache_t *cache, *nextcache; + + //free all portal cache if existing + if (!aasworld.portalcache) return; + //free portal caches + for (i = 0; i < aasworld.numareas; i++) + { + for (cache = aasworld.portalcache[i]; cache; cache = nextcache) + { + nextcache = cache->next; + AAS_FreeRoutingCache(cache); + } //end for + aasworld.portalcache[i] = NULL; + } //end for + FreeMemory(aasworld.portalcache); + aasworld.portalcache = NULL; +} //end of the function AAS_FreeAllPortalCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitPortalCache(void) +{ + // + aasworld.portalcache = (aas_routingcache_t **) GetClearedMemory( + aasworld.numareas * sizeof(aas_routingcache_t *)); +} //end of the function AAS_InitPortalCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitRoutingUpdate(void) +{ + int i, maxreachabilityareas; + + //free routing update fields if already existing + if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate); + // + maxreachabilityareas = 0; + for (i = 0; i < aasworld.numclusters; i++) + { + if (aasworld.clusters[i].numreachabilityareas > maxreachabilityareas) + { + maxreachabilityareas = aasworld.clusters[i].numreachabilityareas; + } //end if + } //end for + //allocate memory for the routing update fields + aasworld.areaupdate = (aas_routingupdate_t *) GetClearedMemory( + maxreachabilityareas * sizeof(aas_routingupdate_t)); + // + if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate); + //allocate memory for the portal update fields + aasworld.portalupdate = (aas_routingupdate_t *) GetClearedMemory( + (aasworld.numportals+1) * sizeof(aas_routingupdate_t)); +} //end of the function AAS_InitRoutingUpdate +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CreateAllRoutingCache(void) +{ + int i, j, t; + + aasworld.initialized = qtrue; + botimport.Print(PRT_MESSAGE, "AAS_CreateAllRoutingCache\n"); + for (i = 1; i < aasworld.numareas; i++) + { + if (!AAS_AreaReachability(i)) continue; + for (j = 1; j < aasworld.numareas; j++) + { + if (i == j) continue; + if (!AAS_AreaReachability(j)) continue; + t = AAS_AreaTravelTimeToGoalArea(i, aasworld.areas[i].center, j, TFL_DEFAULT); + //Log_Write("traveltime from %d to %d is %d", i, j, t); + } //end for + } //end for + aasworld.initialized = qfalse; +} //end of the function AAS_CreateAllRoutingCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +//the route cache header +//this header is followed by numportalcache + numareacache aas_routingcache_t +//structures that store routing cache +typedef struct routecacheheader_s +{ + int ident; + int version; + int numareas; + int numclusters; + int areacrc; + int clustercrc; + int numportalcache; + int numareacache; +} routecacheheader_t; + +#define RCID (('C'<<24)+('R'<<16)+('E'<<8)+'M') +#define RCVERSION 2 + +//void AAS_DecompressVis(byte *in, int numareas, byte *decompressed); +//int AAS_CompressVis(byte *vis, int numareas, byte *dest); + +void AAS_WriteRouteCache(void) +{ + int i, j, numportalcache, numareacache, totalsize; + aas_routingcache_t *cache; + aas_cluster_t *cluster; + fileHandle_t fp; + char filename[MAX_QPATH]; + routecacheheader_t routecacheheader; + + numportalcache = 0; + for (i = 0; i < aasworld.numareas; i++) + { + for (cache = aasworld.portalcache[i]; cache; cache = cache->next) + { + numportalcache++; + } //end for + } //end for + numareacache = 0; + for (i = 0; i < aasworld.numclusters; i++) + { + cluster = &aasworld.clusters[i]; + for (j = 0; j < cluster->numareas; j++) + { + for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next) + { + numareacache++; + } //end for + } //end for + } //end for + // open the file for writing + Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname); + botimport.FS_FOpenFile( filename, &fp, FS_WRITE ); + if (!fp) + { + AAS_Error("Unable to open file: %s\n", filename); + return; + } //end if + //create the header + routecacheheader.ident = RCID; + routecacheheader.version = RCVERSION; + routecacheheader.numareas = aasworld.numareas; + routecacheheader.numclusters = aasworld.numclusters; + routecacheheader.areacrc = CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas ); + routecacheheader.clustercrc = CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters ); + routecacheheader.numportalcache = numportalcache; + routecacheheader.numareacache = numareacache; + //write the header + botimport.FS_Write(&routecacheheader, sizeof(routecacheheader_t), fp); + // + totalsize = 0; + //write all the cache + for (i = 0; i < aasworld.numareas; i++) + { + for (cache = aasworld.portalcache[i]; cache; cache = cache->next) + { + botimport.FS_Write(cache, cache->size, fp); + totalsize += cache->size; + } //end for + } //end for + for (i = 0; i < aasworld.numclusters; i++) + { + cluster = &aasworld.clusters[i]; + for (j = 0; j < cluster->numareas; j++) + { + for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next) + { + botimport.FS_Write(cache, cache->size, fp); + totalsize += cache->size; + } //end for + } //end for + } //end for + // write the visareas + /* + for (i = 0; i < aasworld.numareas; i++) + { + if (!aasworld.areavisibility[i]) { + size = 0; + botimport.FS_Write(&size, sizeof(int), fp); + continue; + } + AAS_DecompressVis( aasworld.areavisibility[i], aasworld.numareas, aasworld.decompressedvis ); + size = AAS_CompressVis( aasworld.decompressedvis, aasworld.numareas, aasworld.decompressedvis ); + botimport.FS_Write(&size, sizeof(int), fp); + botimport.FS_Write(aasworld.decompressedvis, size, fp); + } + */ + // + botimport.FS_FCloseFile(fp); + botimport.Print(PRT_MESSAGE, "\nroute cache written to %s\n", filename); + botimport.Print(PRT_MESSAGE, "written %d bytes of routing cache\n", totalsize); +} //end of the function AAS_WriteRouteCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_routingcache_t *AAS_ReadCache(fileHandle_t fp) +{ + int size; + aas_routingcache_t *cache; + + botimport.FS_Read(&size, sizeof(size), fp); + cache = (aas_routingcache_t *) GetMemory(size); + cache->size = size; + botimport.FS_Read((unsigned char *)cache + sizeof(size), size - sizeof(size), fp); + cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) - sizeof(unsigned short) + + (size - sizeof(aas_routingcache_t) + sizeof(unsigned short)) / 3 * 2; + return cache; +} //end of the function AAS_ReadCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_ReadRouteCache(void) +{ + int i, clusterareanum;//, size; + fileHandle_t fp; + char filename[MAX_QPATH]; + routecacheheader_t routecacheheader; + aas_routingcache_t *cache; + + Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname); + botimport.FS_FOpenFile( filename, &fp, FS_READ ); + if (!fp) + { + return qfalse; + } //end if + botimport.FS_Read(&routecacheheader, sizeof(routecacheheader_t), fp ); + if (routecacheheader.ident != RCID) + { + AAS_Error("%s is not a route cache dump\n"); + return qfalse; + } //end if + if (routecacheheader.version != RCVERSION) + { + AAS_Error("route cache dump has wrong version %d, should be %d", routecacheheader.version, RCVERSION); + return qfalse; + } //end if + if (routecacheheader.numareas != aasworld.numareas) + { + //AAS_Error("route cache dump has wrong number of areas\n"); + return qfalse; + } //end if + if (routecacheheader.numclusters != aasworld.numclusters) + { + //AAS_Error("route cache dump has wrong number of clusters\n"); + return qfalse; + } //end if + if (routecacheheader.areacrc != + CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas )) + { + //AAS_Error("route cache dump area CRC incorrect\n"); + return qfalse; + } //end if + if (routecacheheader.clustercrc != + CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters )) + { + //AAS_Error("route cache dump cluster CRC incorrect\n"); + return qfalse; + } //end if + //read all the portal cache + for (i = 0; i < routecacheheader.numportalcache; i++) + { + cache = AAS_ReadCache(fp); + cache->next = aasworld.portalcache[cache->areanum]; + cache->prev = NULL; + if (aasworld.portalcache[cache->areanum]) + aasworld.portalcache[cache->areanum]->prev = cache; + aasworld.portalcache[cache->areanum] = cache; + } //end for + //read all the cluster area cache + for (i = 0; i < routecacheheader.numareacache; i++) + { + cache = AAS_ReadCache(fp); + clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum); + cache->next = aasworld.clusterareacache[cache->cluster][clusterareanum]; + cache->prev = NULL; + if (aasworld.clusterareacache[cache->cluster][clusterareanum]) + aasworld.clusterareacache[cache->cluster][clusterareanum]->prev = cache; + aasworld.clusterareacache[cache->cluster][clusterareanum] = cache; + } //end for + // read the visareas + /* + aasworld.areavisibility = (byte **) GetClearedMemory(aasworld.numareas * sizeof(byte *)); + aasworld.decompressedvis = (byte *) GetClearedMemory(aasworld.numareas * sizeof(byte)); + for (i = 0; i < aasworld.numareas; i++) + { + botimport.FS_Read(&size, sizeof(size), fp ); + if (size) { + aasworld.areavisibility[i] = (byte *) GetMemory(size); + botimport.FS_Read(aasworld.areavisibility[i], size, fp ); + } + } + */ + // + botimport.FS_FCloseFile(fp); + return qtrue; +} //end of the function AAS_ReadRouteCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#define MAX_REACHABILITYPASSAREAS 32 + +void AAS_InitReachabilityAreas(void) +{ + int i, j, numareas, areas[MAX_REACHABILITYPASSAREAS]; + int numreachareas; + aas_reachability_t *reach; + vec3_t start, end; + + if (aasworld.reachabilityareas) + FreeMemory(aasworld.reachabilityareas); + if (aasworld.reachabilityareaindex) + FreeMemory(aasworld.reachabilityareaindex); + + aasworld.reachabilityareas = (aas_reachabilityareas_t *) + GetClearedMemory(aasworld.reachabilitysize * sizeof(aas_reachabilityareas_t)); + aasworld.reachabilityareaindex = (int *) + GetClearedMemory(aasworld.reachabilitysize * MAX_REACHABILITYPASSAREAS * sizeof(int)); + numreachareas = 0; + for (i = 0; i < aasworld.reachabilitysize; i++) + { + reach = &aasworld.reachability[i]; + numareas = 0; + switch(reach->traveltype & TRAVELTYPE_MASK) + { + //trace areas from start to end + case TRAVEL_BARRIERJUMP: + case TRAVEL_WATERJUMP: + VectorCopy(reach->start, end); + end[2] = reach->end[2]; + numareas = AAS_TraceAreas(reach->start, end, areas, NULL, MAX_REACHABILITYPASSAREAS); + break; + case TRAVEL_WALKOFFLEDGE: + VectorCopy(reach->end, start); + start[2] = reach->start[2]; + numareas = AAS_TraceAreas(start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS); + break; + case TRAVEL_GRAPPLEHOOK: + numareas = AAS_TraceAreas(reach->start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS); + break; + + //trace arch + case TRAVEL_JUMP: break; + case TRAVEL_ROCKETJUMP: break; + case TRAVEL_BFGJUMP: break; + case TRAVEL_JUMPPAD: break; + + //trace from reach->start to entity center, along entity movement + //and from entity center to reach->end + case TRAVEL_ELEVATOR: break; + case TRAVEL_FUNCBOB: break; + + //no areas in between + case TRAVEL_WALK: break; + case TRAVEL_CROUCH: break; + case TRAVEL_LADDER: break; + case TRAVEL_SWIM: break; + case TRAVEL_TELEPORT: break; + default: break; + } //end switch + aasworld.reachabilityareas[i].firstarea = numreachareas; + aasworld.reachabilityareas[i].numareas = numareas; + for (j = 0; j < numareas; j++) + { + aasworld.reachabilityareaindex[numreachareas++] = areas[j]; + } //end for + } //end for +} //end of the function AAS_InitReachabilityAreas +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitRouting(void) +{ + AAS_InitTravelFlagFromType(); + // + AAS_InitAreaContentsTravelFlags(); + //initialize the routing update fields + AAS_InitRoutingUpdate(); + //create reversed reachability links used by the routing update algorithm + AAS_CreateReversedReachability(); + //initialize the cluster cache + AAS_InitClusterAreaCache(); + //initialize portal cache + AAS_InitPortalCache(); + //initialize the area travel times + AAS_CalculateAreaTravelTimes(); + //calculate the maximum travel times through portals + AAS_InitPortalMaxTravelTimes(); + //get the areas reachabilities go through + AAS_InitReachabilityAreas(); + // +#ifdef ROUTING_DEBUG + numareacacheupdates = 0; + numportalcacheupdates = 0; +#endif //ROUTING_DEBUG + // + routingcachesize = 0; + max_routingcachesize = 1024 * (int) LibVarValue("max_routingcache", "4096"); + // read any routing cache if available + AAS_ReadRouteCache(); +} //end of the function AAS_InitRouting +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeRoutingCaches(void) +{ + // free all the existing cluster area cache + AAS_FreeAllClusterAreaCache(); + // free all the existing portal cache + AAS_FreeAllPortalCache(); + // free cached travel times within areas + if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes); + aasworld.areatraveltimes = NULL; + // free cached maximum travel time through cluster portals + if (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes); + aasworld.portalmaxtraveltimes = NULL; + // free reversed reachability links + if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability); + aasworld.reversedreachability = NULL; + // free routing algorithm memory + if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate); + aasworld.areaupdate = NULL; + if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate); + aasworld.portalupdate = NULL; + // free lists with areas the reachabilities go through + if (aasworld.reachabilityareas) FreeMemory(aasworld.reachabilityareas); + aasworld.reachabilityareas = NULL; + // free the reachability area index + if (aasworld.reachabilityareaindex) FreeMemory(aasworld.reachabilityareaindex); + aasworld.reachabilityareaindex = NULL; + // free area contents travel flags look up table + if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags); + aasworld.areacontentstravelflags = NULL; +} //end of the function AAS_FreeRoutingCaches +//=========================================================================== +// update the given routing cache +// +// Parameter: areacache : routing cache to update +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_UpdateAreaRoutingCache(aas_routingcache_t *areacache) +{ + int i, nextareanum, cluster, badtravelflags, clusterareanum, linknum; + int numreachabilityareas; + unsigned short int t, startareatraveltimes[128]; //NOTE: not more than 128 reachabilities per area allowed + aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; + aas_reachability_t *reach; + aas_reversedreachability_t *revreach; + aas_reversedlink_t *revlink; + +#ifdef ROUTING_DEBUG + numareacacheupdates++; +#endif //ROUTING_DEBUG + //number of reachability areas within this cluster + numreachabilityareas = aasworld.clusters[areacache->cluster].numreachabilityareas; + // + aasworld.frameroutingupdates++; + //clear the routing update fields +// Com_Memset(aasworld.areaupdate, 0, aasworld.numareas * sizeof(aas_routingupdate_t)); + // + badtravelflags = ~areacache->travelflags; + // + clusterareanum = AAS_ClusterAreaNum(areacache->cluster, areacache->areanum); + if (clusterareanum >= numreachabilityareas) return; + // + Com_Memset(startareatraveltimes, 0, sizeof(startareatraveltimes)); + // + curupdate = &aasworld.areaupdate[clusterareanum]; + curupdate->areanum = areacache->areanum; + //VectorCopy(areacache->origin, curupdate->start); + curupdate->areatraveltimes = startareatraveltimes; + curupdate->tmptraveltime = areacache->starttraveltime; + // + areacache->traveltimes[clusterareanum] = areacache->starttraveltime; + //put the area to start with in the current read list + curupdate->next = NULL; + curupdate->prev = NULL; + updateliststart = curupdate; + updatelistend = curupdate; + //while there are updates in the current list + while (updateliststart) + { + curupdate = updateliststart; + // + if (curupdate->next) curupdate->next->prev = NULL; + else updatelistend = NULL; + updateliststart = curupdate->next; + // + curupdate->inlist = qfalse; + //check all reversed reachability links + revreach = &aasworld.reversedreachability[curupdate->areanum]; + // + for (i = 0, revlink = revreach->first; revlink; revlink = revlink->next, i++) + { + linknum = revlink->linknum; + reach = &aasworld.reachability[linknum]; + //if there is used an undesired travel type + if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue; + //if not allowed to enter the next area + if (aasworld.areasettings[reach->areanum].areaflags & AREA_DISABLED) continue; + //if the next area has a not allowed travel flag + if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue; + //number of the area the reversed reachability leads to + nextareanum = revlink->areanum; + //get the cluster number of the area + cluster = aasworld.areasettings[nextareanum].cluster; + //don't leave the cluster + if (cluster > 0 && cluster != areacache->cluster) continue; + //get the number of the area in the cluster + clusterareanum = AAS_ClusterAreaNum(areacache->cluster, nextareanum); + if (clusterareanum >= numreachabilityareas) continue; + //time already travelled plus the traveltime through + //the current area plus the travel time from the reachability + t = curupdate->tmptraveltime + + //AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->end) + + curupdate->areatraveltimes[i] + + reach->traveltime; + // + if (!areacache->traveltimes[clusterareanum] || + areacache->traveltimes[clusterareanum] > t) + { + areacache->traveltimes[clusterareanum] = t; + areacache->reachabilities[clusterareanum] = linknum - aasworld.areasettings[nextareanum].firstreachablearea; + nextupdate = &aasworld.areaupdate[clusterareanum]; + nextupdate->areanum = nextareanum; + nextupdate->tmptraveltime = t; + //VectorCopy(reach->start, nextupdate->start); + nextupdate->areatraveltimes = aasworld.areatraveltimes[nextareanum][linknum - + aasworld.areasettings[nextareanum].firstreachablearea]; + if (!nextupdate->inlist) + { + // we add the update to the end of the list + // we could also use a B+ tree to have a real sorted list + // on travel time which makes for faster routing updates + nextupdate->next = NULL; + nextupdate->prev = updatelistend; + if (updatelistend) updatelistend->next = nextupdate; + else updateliststart = nextupdate; + updatelistend = nextupdate; + nextupdate->inlist = qtrue; + } //end if + } //end if + } //end for + } //end while +} //end of the function AAS_UpdateAreaRoutingCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_routingcache_t *AAS_GetAreaRoutingCache(int clusternum, int areanum, int travelflags) +{ + int clusterareanum; + aas_routingcache_t *cache, *clustercache; + + //number of the area in the cluster + clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); + //pointer to the cache for the area in the cluster + clustercache = aasworld.clusterareacache[clusternum][clusterareanum]; + //find the cache without undesired travel flags + for (cache = clustercache; cache; cache = cache->next) + { + //if there aren't used any undesired travel types for the cache + if (cache->travelflags == travelflags) break; + } //end for + //if there was no cache + if (!cache) + { + cache = AAS_AllocRoutingCache(aasworld.clusters[clusternum].numreachabilityareas); + cache->cluster = clusternum; + cache->areanum = areanum; + VectorCopy(aasworld.areas[areanum].center, cache->origin); + cache->starttraveltime = 1; + cache->travelflags = travelflags; + cache->prev = NULL; + cache->next = clustercache; + if (clustercache) clustercache->prev = cache; + aasworld.clusterareacache[clusternum][clusterareanum] = cache; + AAS_UpdateAreaRoutingCache(cache); + } //end if + else + { + AAS_UnlinkCache(cache); + } //end else + //the cache has been accessed + cache->time = AAS_RoutingTime(); + cache->type = CACHETYPE_AREA; + AAS_LinkCache(cache); + return cache; +} //end of the function AAS_GetAreaRoutingCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_UpdatePortalRoutingCache(aas_routingcache_t *portalcache) +{ + int i, portalnum, clusterareanum, clusternum; + unsigned short int t; + aas_portal_t *portal; + aas_cluster_t *cluster; + aas_routingcache_t *cache; + aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; + +#ifdef ROUTING_DEBUG + numportalcacheupdates++; +#endif //ROUTING_DEBUG + //clear the routing update fields +// Com_Memset(aasworld.portalupdate, 0, (aasworld.numportals+1) * sizeof(aas_routingupdate_t)); + // + curupdate = &aasworld.portalupdate[aasworld.numportals]; + curupdate->cluster = portalcache->cluster; + curupdate->areanum = portalcache->areanum; + curupdate->tmptraveltime = portalcache->starttraveltime; + //if the start area is a cluster portal, store the travel time for that portal + clusternum = aasworld.areasettings[portalcache->areanum].cluster; + if (clusternum < 0) + { + portalcache->traveltimes[-clusternum] = portalcache->starttraveltime; + } //end if + //put the area to start with in the current read list + curupdate->next = NULL; + curupdate->prev = NULL; + updateliststart = curupdate; + updatelistend = curupdate; + //while there are updates in the current list + while (updateliststart) + { + curupdate = updateliststart; + //remove the current update from the list + if (curupdate->next) curupdate->next->prev = NULL; + else updatelistend = NULL; + updateliststart = curupdate->next; + //current update is removed from the list + curupdate->inlist = qfalse; + // + cluster = &aasworld.clusters[curupdate->cluster]; + // + cache = AAS_GetAreaRoutingCache(curupdate->cluster, + curupdate->areanum, portalcache->travelflags); + //take all portals of the cluster + for (i = 0; i < cluster->numportals; i++) + { + portalnum = aasworld.portalindex[cluster->firstportal + i]; + portal = &aasworld.portals[portalnum]; + //if this is the portal of the current update continue + if (portal->areanum == curupdate->areanum) continue; + // + clusterareanum = AAS_ClusterAreaNum(curupdate->cluster, portal->areanum); + if (clusterareanum >= cluster->numreachabilityareas) continue; + // + t = cache->traveltimes[clusterareanum]; + if (!t) continue; + t += curupdate->tmptraveltime; + // + if (!portalcache->traveltimes[portalnum] || + portalcache->traveltimes[portalnum] > t) + { + portalcache->traveltimes[portalnum] = t; + nextupdate = &aasworld.portalupdate[portalnum]; + if (portal->frontcluster == curupdate->cluster) + { + nextupdate->cluster = portal->backcluster; + } //end if + else + { + nextupdate->cluster = portal->frontcluster; + } //end else + nextupdate->areanum = portal->areanum; + //add travel time through the actual portal area for the next update + nextupdate->tmptraveltime = t + aasworld.portalmaxtraveltimes[portalnum]; + if (!nextupdate->inlist) + { + // we add the update to the end of the list + // we could also use a B+ tree to have a real sorted list + // on travel time which makes for faster routing updates + nextupdate->next = NULL; + nextupdate->prev = updatelistend; + if (updatelistend) updatelistend->next = nextupdate; + else updateliststart = nextupdate; + updatelistend = nextupdate; + nextupdate->inlist = qtrue; + } //end if + } //end if + } //end for + } //end while +} //end of the function AAS_UpdatePortalRoutingCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_routingcache_t *AAS_GetPortalRoutingCache(int clusternum, int areanum, int travelflags) +{ + aas_routingcache_t *cache; + + //find the cached portal routing if existing + for (cache = aasworld.portalcache[areanum]; cache; cache = cache->next) + { + if (cache->travelflags == travelflags) break; + } //end for + //if the portal routing isn't cached + if (!cache) + { + cache = AAS_AllocRoutingCache(aasworld.numportals); + cache->cluster = clusternum; + cache->areanum = areanum; + VectorCopy(aasworld.areas[areanum].center, cache->origin); + cache->starttraveltime = 1; + cache->travelflags = travelflags; + //add the cache to the cache list + cache->prev = NULL; + cache->next = aasworld.portalcache[areanum]; + if (aasworld.portalcache[areanum]) aasworld.portalcache[areanum]->prev = cache; + aasworld.portalcache[areanum] = cache; + //update the cache + AAS_UpdatePortalRoutingCache(cache); + } //end if + else + { + AAS_UnlinkCache(cache); + } //end else + //the cache has been accessed + cache->time = AAS_RoutingTime(); + cache->type = CACHETYPE_PORTAL; + AAS_LinkCache(cache); + return cache; +} //end of the function AAS_GetPortalRoutingCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaRouteToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags, int *traveltime, int *reachnum) +{ + int clusternum, goalclusternum, portalnum, i, clusterareanum, bestreachnum; + unsigned short int t, besttime; + aas_portal_t *portal; + aas_cluster_t *cluster; + aas_routingcache_t *areacache, *portalcache; + aas_reachability_t *reach; + + if (!aasworld.initialized) return qfalse; + + if (areanum == goalareanum) + { + *traveltime = 1; + *reachnum = 0; + return qtrue; + } + // + if (areanum <= 0 || areanum >= aasworld.numareas) + { + if (bot_developer) + { + botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: areanum %d out of range\n", areanum); + } //end if + return qfalse; + } //end if + if (goalareanum <= 0 || goalareanum >= aasworld.numareas) + { + if (bot_developer) + { + botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: goalareanum %d out of range\n", goalareanum); + } //end if + return qfalse; + } //end if + // make sure the routing cache doesn't grow to large + while(AvailableMemory() < 1 * 1024 * 1024) { + if (!AAS_FreeOldestCache()) break; + } + // + if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goalareanum)) + { + travelflags |= TFL_DONOTENTER; + } //end if + //NOTE: the number of routing updates is limited per frame + /* + if (aasworld.frameroutingupdates > MAX_FRAMEROUTINGUPDATES) + { +#ifdef DEBUG + //Log_Write("WARNING: AAS_AreaTravelTimeToGoalArea: frame routing updates overflowed"); +#endif + return 0; + } //end if + */ + // + clusternum = aasworld.areasettings[areanum].cluster; + goalclusternum = aasworld.areasettings[goalareanum].cluster; + //check if the area is a portal of the goal area cluster + if (clusternum < 0 && goalclusternum > 0) + { + portal = &aasworld.portals[-clusternum]; + if (portal->frontcluster == goalclusternum || + portal->backcluster == goalclusternum) + { + clusternum = goalclusternum; + } //end if + } //end if + //check if the goalarea is a portal of the area cluster + else if (clusternum > 0 && goalclusternum < 0) + { + portal = &aasworld.portals[-goalclusternum]; + if (portal->frontcluster == clusternum || + portal->backcluster == clusternum) + { + goalclusternum = clusternum; + } //end if + } //end if + //if both areas are in the same cluster + //NOTE: there might be a shorter route via another cluster!!! but we don't care + if (clusternum > 0 && goalclusternum > 0 && clusternum == goalclusternum) + { + // + areacache = AAS_GetAreaRoutingCache(clusternum, goalareanum, travelflags); + //the number of the area in the cluster + clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); + //the cluster the area is in + cluster = &aasworld.clusters[clusternum]; + //if the area is NOT a reachability area + if (clusterareanum >= cluster->numreachabilityareas) return 0; + //if it is possible to travel to the goal area through this cluster + if (areacache->traveltimes[clusterareanum] != 0) + { + *reachnum = aasworld.areasettings[areanum].firstreachablearea + + areacache->reachabilities[clusterareanum]; + if (!origin) { + *traveltime = areacache->traveltimes[clusterareanum]; + return qtrue; + } + reach = &aasworld.reachability[*reachnum]; + *traveltime = areacache->traveltimes[clusterareanum] + + AAS_AreaTravelTime(areanum, origin, reach->start); + // + return qtrue; + } //end if + } //end if + // + clusternum = aasworld.areasettings[areanum].cluster; + goalclusternum = aasworld.areasettings[goalareanum].cluster; + //if the goal area is a portal + if (goalclusternum < 0) + { + //just assume the goal area is part of the front cluster + portal = &aasworld.portals[-goalclusternum]; + goalclusternum = portal->frontcluster; + } //end if + //get the portal routing cache + portalcache = AAS_GetPortalRoutingCache(goalclusternum, goalareanum, travelflags); + //if the area is a cluster portal, read directly from the portal cache + if (clusternum < 0) + { + *traveltime = portalcache->traveltimes[-clusternum]; + *reachnum = aasworld.areasettings[areanum].firstreachablearea + + portalcache->reachabilities[-clusternum]; + return qtrue; + } //end if + // + besttime = 0; + bestreachnum = -1; + //the cluster the area is in + cluster = &aasworld.clusters[clusternum]; + //find the portal of the area cluster leading towards the goal area + for (i = 0; i < cluster->numportals; i++) + { + portalnum = aasworld.portalindex[cluster->firstportal + i]; + //if the goal area isn't reachable from the portal + if (!portalcache->traveltimes[portalnum]) continue; + // + portal = &aasworld.portals[portalnum]; + //get the cache of the portal area + areacache = AAS_GetAreaRoutingCache(clusternum, portal->areanum, travelflags); + //current area inside the current cluster + clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); + //if the area is NOT a reachability area + if (clusterareanum >= cluster->numreachabilityareas) continue; + //if the portal is NOT reachable from this area + if (!areacache->traveltimes[clusterareanum]) continue; + //total travel time is the travel time the portal area is from + //the goal area plus the travel time towards the portal area + t = portalcache->traveltimes[portalnum] + areacache->traveltimes[clusterareanum]; + //FIXME: add the exact travel time through the actual portal area + //NOTE: for now we just add the largest travel time through the portal area + // because we can't directly calculate the exact travel time + // to be more specific we don't know which reachability was used to travel + // into the portal area + t += aasworld.portalmaxtraveltimes[portalnum]; + // + if (origin) + { + *reachnum = aasworld.areasettings[areanum].firstreachablearea + + areacache->reachabilities[clusterareanum]; + reach = aasworld.reachability + *reachnum; + t += AAS_AreaTravelTime(areanum, origin, reach->start); + } //end if + //if the time is better than the one already found + if (!besttime || t < besttime) + { + bestreachnum = *reachnum; + besttime = t; + } //end if + } //end for + if (bestreachnum < 0) { + return qfalse; + } + *reachnum = bestreachnum; + *traveltime = besttime; + return qtrue; +} //end of the function AAS_AreaRouteToGoalArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags) +{ + int traveltime, reachnum; + + if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum)) + { + return traveltime; + } + return 0; +} //end of the function AAS_AreaTravelTimeToGoalArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaReachabilityToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags) +{ + int traveltime, reachnum; + + if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum)) + { + return reachnum; + } + return 0; +} //end of the function AAS_AreaReachabilityToGoalArea +//=========================================================================== +// predict the route and stop on one of the stop events +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin, + int goalareanum, int travelflags, int maxareas, int maxtime, + int stopevent, int stopcontents, int stoptfl, int stopareanum) +{ + int curareanum, reachnum, i, j, testareanum; + vec3_t curorigin; + aas_reachability_t *reach; + aas_reachabilityareas_t *reachareas; + + //init output + route->stopevent = RSE_NONE; + route->endarea = goalareanum; + route->endcontents = 0; + route->endtravelflags = 0; + VectorCopy(origin, route->endpos); + route->time = 0; + + curareanum = areanum; + VectorCopy(origin, curorigin); + + for (i = 0; curareanum != goalareanum && (!maxareas || i < maxareas) && i < aasworld.numareas; i++) + { + reachnum = AAS_AreaReachabilityToGoalArea(curareanum, curorigin, goalareanum, travelflags); + if (!reachnum) + { + route->stopevent = RSE_NOROUTE; + return qfalse; + } //end if + reach = &aasworld.reachability[reachnum]; + // + if (stopevent & RSE_USETRAVELTYPE) + { + if (AAS_TravelFlagForType_inline(reach->traveltype) & stoptfl) + { + route->stopevent = RSE_USETRAVELTYPE; + route->endarea = curareanum; + route->endcontents = aasworld.areasettings[curareanum].contents; + route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype); + VectorCopy(reach->start, route->endpos); + return qtrue; + } //end if + if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & stoptfl) + { + route->stopevent = RSE_USETRAVELTYPE; + route->endarea = reach->areanum; + route->endcontents = aasworld.areasettings[reach->areanum].contents; + route->endtravelflags = AAS_AreaContentsTravelFlags_inline(reach->areanum); + VectorCopy(reach->end, route->endpos); + route->time += AAS_AreaTravelTime(areanum, origin, reach->start); + route->time += reach->traveltime; + return qtrue; + } //end if + } //end if + reachareas = &aasworld.reachabilityareas[reachnum]; + for (j = 0; j < reachareas->numareas + 1; j++) + { + if (j >= reachareas->numareas) + testareanum = reach->areanum; + else + testareanum = aasworld.reachabilityareaindex[reachareas->firstarea + j]; + if (stopevent & RSE_ENTERCONTENTS) + { + if (aasworld.areasettings[testareanum].contents & stopcontents) + { + route->stopevent = RSE_ENTERCONTENTS; + route->endarea = testareanum; + route->endcontents = aasworld.areasettings[testareanum].contents; + VectorCopy(reach->end, route->endpos); + route->time += AAS_AreaTravelTime(areanum, origin, reach->start); + route->time += reach->traveltime; + return qtrue; + } //end if + } //end if + if (stopevent & RSE_ENTERAREA) + { + if (testareanum == stopareanum) + { + route->stopevent = RSE_ENTERAREA; + route->endarea = testareanum; + route->endcontents = aasworld.areasettings[testareanum].contents; + VectorCopy(reach->start, route->endpos); + return qtrue; + } //end if + } //end if + } //end for + + route->time += AAS_AreaTravelTime(areanum, origin, reach->start); + route->time += reach->traveltime; + route->endarea = reach->areanum; + route->endcontents = aasworld.areasettings[reach->areanum].contents; + route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype); + VectorCopy(reach->end, route->endpos); + // + curareanum = reach->areanum; + VectorCopy(reach->end, curorigin); + // + if (maxtime && route->time > maxtime) + break; + } //end while + if (curareanum != goalareanum) + return qfalse; + return qtrue; +} //end of the function AAS_PredictRoute +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BridgeWalkable(int areanum) +{ + return qfalse; +} //end of the function AAS_BridgeWalkable +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach) +{ + if (!aasworld.initialized) + { + Com_Memset(reach, 0, sizeof(aas_reachability_t)); + return; + } //end if + if (num < 0 || num >= aasworld.reachabilitysize) + { + Com_Memset(reach, 0, sizeof(aas_reachability_t)); + return; + } //end if + Com_Memcpy(reach, &aasworld.reachability[num], sizeof(aas_reachability_t));; +} //end of the function AAS_ReachabilityFromNum +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NextAreaReachability(int areanum, int reachnum) +{ + aas_areasettings_t *settings; + + if (!aasworld.initialized) return 0; + + if (areanum <= 0 || areanum >= aasworld.numareas) + { + botimport.Print(PRT_ERROR, "AAS_NextAreaReachability: areanum %d out of range\n", areanum); + return 0; + } //end if + + settings = &aasworld.areasettings[areanum]; + if (!reachnum) + { + return settings->firstreachablearea; + } //end if + if (reachnum < settings->firstreachablearea) + { + botimport.Print(PRT_FATAL, "AAS_NextAreaReachability: reachnum < settings->firstreachableara"); + return 0; + } //end if + reachnum++; + if (reachnum >= settings->firstreachablearea + settings->numreachableareas) + { + return 0; + } //end if + return reachnum; +} //end of the function AAS_NextAreaReachability +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NextModelReachability(int num, int modelnum) +{ + int i; + + if (num <= 0) num = 1; + else if (num >= aasworld.reachabilitysize) return 0; + else num++; + // + for (i = num; i < aasworld.reachabilitysize; i++) + { + if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) + { + if (aasworld.reachability[i].facenum == modelnum) return i; + } //end if + else if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB) + { + if ((aasworld.reachability[i].facenum & 0x0000FFFF) == modelnum) return i; + } //end if + } //end for + return 0; +} //end of the function AAS_NextModelReachability +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin) +{ + int i, n, t; + vec3_t start, end; + aas_trace_t trace; + + //if the area has no reachabilities + if (!AAS_AreaReachability(areanum)) return qfalse; + // + n = aasworld.numareas * random(); + for (i = 0; i < aasworld.numareas; i++) + { + if (n <= 0) n = 1; + if (n >= aasworld.numareas) n = 1; + if (AAS_AreaReachability(n)) + { + t = AAS_AreaTravelTimeToGoalArea(areanum, aasworld.areas[areanum].center, n, travelflags); + //if the goal is reachable + if (t > 0) + { + if (AAS_AreaSwim(n)) + { + *goalareanum = n; + VectorCopy(aasworld.areas[n].center, goalorigin); + //botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum); + return qtrue; + } //end if + VectorCopy(aasworld.areas[n].center, start); + if (!AAS_PointAreaNum(start)) + Log_Write("area %d center %f %f %f in solid?", n, start[0], start[1], start[2]); + VectorCopy(start, end); + end[2] -= 300; + trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); + if (!trace.startsolid && trace.fraction < 1 && AAS_PointAreaNum(trace.endpos) == n) + { + if (AAS_AreaGroundFaceArea(n) > 300) + { + *goalareanum = n; + VectorCopy(trace.endpos, goalorigin); + //botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum); + return qtrue; + } //end if + } //end if + } //end if + } //end if + n++; + } //end for + return qfalse; +} //end of the function AAS_RandomGoalArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaVisible(int srcarea, int destarea) +{ + return qfalse; +} //end of the function AAS_AreaVisible +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float DistancePointToLine(vec3_t v1, vec3_t v2, vec3_t point) +{ + vec3_t vec, p2; + + AAS_ProjectPointOntoVector(point, v1, v2, p2); + VectorSubtract(point, p2, vec); + return VectorLength(vec); +} //end of the function DistancePointToLine +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, vec3_t enemyorigin, int enemyareanum, int travelflags) +{ + int i, j, nextareanum, badtravelflags, numreach, bestarea; + unsigned short int t, besttraveltime; + static unsigned short int *hidetraveltimes; + aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; + aas_reachability_t *reach; + float dist1, dist2; + vec3_t v1, v2, p; + qboolean startVisible; + + // + if (!hidetraveltimes) + { + hidetraveltimes = (unsigned short int *) GetClearedMemory(aasworld.numareas * sizeof(unsigned short int)); + } //end if + else + { + Com_Memset(hidetraveltimes, 0, aasworld.numareas * sizeof(unsigned short int)); + } //end else + besttraveltime = 0; + bestarea = 0; + //assume visible + startVisible = qtrue; + // + badtravelflags = ~travelflags; + // + curupdate = &aasworld.areaupdate[areanum]; + curupdate->areanum = areanum; + VectorCopy(origin, curupdate->start); + curupdate->areatraveltimes = aasworld.areatraveltimes[areanum][0]; + curupdate->tmptraveltime = 0; + //put the area to start with in the current read list + curupdate->next = NULL; + curupdate->prev = NULL; + updateliststart = curupdate; + updatelistend = curupdate; + //while there are updates in the list + while (updateliststart) + { + curupdate = updateliststart; + // + if (curupdate->next) curupdate->next->prev = NULL; + else updatelistend = NULL; + updateliststart = curupdate->next; + // + curupdate->inlist = qfalse; + //check all reversed reachability links + numreach = aasworld.areasettings[curupdate->areanum].numreachableareas; + reach = &aasworld.reachability[aasworld.areasettings[curupdate->areanum].firstreachablearea]; + // + for (i = 0; i < numreach; i++, reach++) + { + //if an undesired travel type is used + if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue; + // + if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue; + //number of the area the reachability leads to + nextareanum = reach->areanum; + // if this moves us into the enemies area, skip it + if (nextareanum == enemyareanum) continue; + //time already travelled plus the traveltime through + //the current area plus the travel time from the reachability + t = curupdate->tmptraveltime + + AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->start) + + reach->traveltime; + + //avoid going near the enemy + AAS_ProjectPointOntoVector(enemyorigin, curupdate->start, reach->end, p); + for (j = 0; j < 3; j++) + if ((p[j] > curupdate->start[j] && p[j] > reach->end[j]) || + (p[j] < curupdate->start[j] && p[j] < reach->end[j])) + break; + if (j < 3) + { + VectorSubtract(enemyorigin, reach->end, v2); + } //end if + else + { + VectorSubtract(enemyorigin, p, v2); + } //end else + dist2 = VectorLength(v2); + //never go through the enemy + if (dist2 < 40) continue; + // + VectorSubtract(enemyorigin, curupdate->start, v1); + dist1 = VectorLength(v1); + // + if (dist2 < dist1) + { + t += (dist1 - dist2) * 10; + } + // if we weren't visible when starting, make sure we don't move into their view + if (!startVisible && AAS_AreaVisible(enemyareanum, nextareanum)) { + continue; + } + // + if (besttraveltime && t >= besttraveltime) continue; + // + if (!hidetraveltimes[nextareanum] || + hidetraveltimes[nextareanum] > t) + { + //if the nextarea is not visible from the enemy area + if (!AAS_AreaVisible(enemyareanum, nextareanum)) + { + besttraveltime = t; + bestarea = nextareanum; + } //end if + hidetraveltimes[nextareanum] = t; + nextupdate = &aasworld.areaupdate[nextareanum]; + nextupdate->areanum = nextareanum; + nextupdate->tmptraveltime = t; + //remember where we entered this area + VectorCopy(reach->end, nextupdate->start); + //if this update is not in the list yet + if (!nextupdate->inlist) + { + //add the new update to the end of the list + nextupdate->next = NULL; + nextupdate->prev = updatelistend; + if (updatelistend) updatelistend->next = nextupdate; + else updateliststart = nextupdate; + updatelistend = nextupdate; + nextupdate->inlist = qtrue; + } //end if + } //end if + } //end for + } //end while + return bestarea; +} //end of the function AAS_NearestHideArea diff --git a/reaction/engine/code/botlib/be_aas_route.h b/reaction/engine/code/botlib/be_aas_route.h new file mode 100644 index 00000000..8805c66f --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_route.h @@ -0,0 +1,67 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_route.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_route.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +//initialize the AAS routing +void AAS_InitRouting(void); +//free the AAS routing caches +void AAS_FreeRoutingCaches(void); +//returns the travel time from start to end in the given area +unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end); +// +void AAS_CreateAllRoutingCache(void); +void AAS_WriteRouteCache(void); +// +void AAS_RoutingInfo(void); +#endif //AASINTERN + +//returns the travel flag for the given travel type +int AAS_TravelFlagForType(int traveltype); +//return the travel flag(s) for traveling through this area +int AAS_AreaContentsTravelFlags(int areanum); +//returns the index of the next reachability for the given area +int AAS_NextAreaReachability(int areanum, int reachnum); +//returns the reachability with the given index +void AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach); +//returns a random goal area and goal origin +int AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin); +//enable or disable an area for routing +int AAS_EnableRoutingArea(int areanum, int enable); +//returns the travel time within the given area from start to end +unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end); +//returns the travel time from the area to the goal area using the given travel flags +int AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags); +//predict a route up to a stop event +int AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin, + int goalareanum, int travelflags, int maxareas, int maxtime, + int stopevent, int stopcontents, int stoptfl, int stopareanum); + + diff --git a/reaction/engine/code/botlib/be_aas_routealt.c b/reaction/engine/code/botlib/be_aas_routealt.c new file mode 100644 index 00000000..e4f79eec --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_routealt.c @@ -0,0 +1,240 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_routealt.c + * + * desc: AAS + * + * $Archive: /MissionPack/code/botlib/be_aas_routealt.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_utils.h" +#include "l_memory.h" +#include "l_log.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_aas_def.h" + +#define ENABLE_ALTROUTING +//#define ALTROUTE_DEBUG + +typedef struct midrangearea_s +{ + int valid; + unsigned short starttime; + unsigned short goaltime; +} midrangearea_t; + +midrangearea_t *midrangeareas; +int *clusterareas; +int numclusterareas; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_AltRoutingFloodCluster_r(int areanum) +{ + int i, otherareanum; + aas_area_t *area; + aas_face_t *face; + + //add the current area to the areas of the current cluster + clusterareas[numclusterareas] = areanum; + numclusterareas++; + //remove the area from the mid range areas + midrangeareas[areanum].valid = qfalse; + //flood to other areas through the faces of this area + area = &aasworld.areas[areanum]; + for (i = 0; i < area->numfaces; i++) + { + face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])]; + //get the area at the other side of the face + if (face->frontarea == areanum) otherareanum = face->backarea; + else otherareanum = face->frontarea; + //if there is an area at the other side of this face + if (!otherareanum) continue; + //if the other area is not a midrange area + if (!midrangeareas[otherareanum].valid) continue; + // + AAS_AltRoutingFloodCluster_r(otherareanum); + } //end for +} //end of the function AAS_AltRoutingFloodCluster_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, + aas_altroutegoal_t *altroutegoals, int maxaltroutegoals, + int type) +{ +#ifndef ENABLE_ALTROUTING + return 0; +#else + int i, j, bestareanum; + int numaltroutegoals, nummidrangeareas; + int starttime, goaltime, goaltraveltime; + float dist, bestdist; + vec3_t mid, dir; +#ifdef ALTROUTE_DEBUG + int startmillisecs; + + startmillisecs = Sys_MilliSeconds(); +#endif + + if (!startareanum || !goalareanum) + return 0; + //travel time towards the goal area + goaltraveltime = AAS_AreaTravelTimeToGoalArea(startareanum, start, goalareanum, travelflags); + //clear the midrange areas + Com_Memset(midrangeareas, 0, aasworld.numareas * sizeof(midrangearea_t)); + numaltroutegoals = 0; + // + nummidrangeareas = 0; + // + for (i = 1; i < aasworld.numareas; i++) + { + // + if (!(type & ALTROUTEGOAL_ALL)) + { + if (!(type & ALTROUTEGOAL_CLUSTERPORTALS && (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL))) + { + if (!(type & ALTROUTEGOAL_VIEWPORTALS && (aasworld.areasettings[i].contents & AREACONTENTS_VIEWPORTAL))) + { + continue; + } //end if + } //end if + } //end if + //if the area has no reachabilities + if (!AAS_AreaReachability(i)) continue; + //tavel time from the area to the start area + starttime = AAS_AreaTravelTimeToGoalArea(startareanum, start, i, travelflags); + if (!starttime) continue; + //if the travel time from the start to the area is greater than the shortest goal travel time + if (starttime > (float) 1.1 * goaltraveltime) continue; + //travel time from the area to the goal area + goaltime = AAS_AreaTravelTimeToGoalArea(i, NULL, goalareanum, travelflags); + if (!goaltime) continue; + //if the travel time from the area to the goal is greater than the shortest goal travel time + if (goaltime > (float) 0.8 * goaltraveltime) continue; + //this is a mid range area + midrangeareas[i].valid = qtrue; + midrangeareas[i].starttime = starttime; + midrangeareas[i].goaltime = goaltime; + Log_Write("%d midrange area %d", nummidrangeareas, i); + nummidrangeareas++; + } //end for + // + for (i = 1; i < aasworld.numareas; i++) + { + if (!midrangeareas[i].valid) continue; + //get the areas in one cluster + numclusterareas = 0; + AAS_AltRoutingFloodCluster_r(i); + //now we've got a cluster with areas through which an alternative route could go + //get the 'center' of the cluster + VectorClear(mid); + for (j = 0; j < numclusterareas; j++) + { + VectorAdd(mid, aasworld.areas[clusterareas[j]].center, mid); + } //end for + VectorScale(mid, 1.0 / numclusterareas, mid); + //get the area closest to the center of the cluster + bestdist = 999999; + bestareanum = 0; + for (j = 0; j < numclusterareas; j++) + { + VectorSubtract(mid, aasworld.areas[clusterareas[j]].center, dir); + dist = VectorLength(dir); + if (dist < bestdist) + { + bestdist = dist; + bestareanum = clusterareas[j]; + } //end if + } //end for + //now we've got an area for an alternative route + //FIXME: add alternative goal origin + VectorCopy(aasworld.areas[bestareanum].center, altroutegoals[numaltroutegoals].origin); + altroutegoals[numaltroutegoals].areanum = bestareanum; + altroutegoals[numaltroutegoals].starttraveltime = midrangeareas[bestareanum].starttime; + altroutegoals[numaltroutegoals].goaltraveltime = midrangeareas[bestareanum].goaltime; + altroutegoals[numaltroutegoals].extratraveltime = + (midrangeareas[bestareanum].starttime + midrangeareas[bestareanum].goaltime) - + goaltraveltime; + numaltroutegoals++; + // +#ifdef ALTROUTE_DEBUG + AAS_ShowAreaPolygons(bestareanum, 1, qtrue); +#endif + //don't return more than the maximum alternative route goals + if (numaltroutegoals >= maxaltroutegoals) break; + } //end for +#ifdef ALTROUTE_DEBUG + botimport.Print(PRT_MESSAGE, "alternative route goals in %d msec\n", Sys_MilliSeconds() - startmillisecs); +#endif + return numaltroutegoals; +#endif +} //end of the function AAS_AlternativeRouteGoals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitAlternativeRouting(void) +{ +#ifdef ENABLE_ALTROUTING + if (midrangeareas) FreeMemory(midrangeareas); + midrangeareas = (midrangearea_t *) GetMemory(aasworld.numareas * sizeof(midrangearea_t)); + if (clusterareas) FreeMemory(clusterareas); + clusterareas = (int *) GetMemory(aasworld.numareas * sizeof(int)); +#endif +} //end of the function AAS_InitAlternativeRouting +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShutdownAlternativeRouting(void) +{ +#ifdef ENABLE_ALTROUTING + if (midrangeareas) FreeMemory(midrangeareas); + midrangeareas = NULL; + if (clusterareas) FreeMemory(clusterareas); + clusterareas = NULL; + numclusterareas = 0; +#endif +} //end of the function AAS_ShutdownAlternativeRouting diff --git a/reaction/engine/code/botlib/be_aas_routealt.h b/reaction/engine/code/botlib/be_aas_routealt.h new file mode 100644 index 00000000..160966a9 --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_routealt.h @@ -0,0 +1,40 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_routealt.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_routealt.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +void AAS_InitAlternativeRouting(void); +void AAS_ShutdownAlternativeRouting(void); +#endif //AASINTERN + + +int AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, + aas_altroutegoal_t *altroutegoals, int maxaltroutegoals, + int type); diff --git a/reaction/engine/code/botlib/be_aas_sample.c b/reaction/engine/code/botlib/be_aas_sample.c new file mode 100644 index 00000000..6c7adec5 --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_sample.c @@ -0,0 +1,1393 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_sample.c + * + * desc: AAS environment sampling + * + * $Archive: /MissionPack/code/botlib/be_aas_sample.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#ifndef BSPC +#include "l_libvar.h" +#endif +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_interface.h" +#include "be_aas_funcs.h" +#include "be_aas_def.h" + + +//#define AAS_SAMPLE_DEBUG + +#define BBOX_NORMAL_EPSILON 0.001 + +#define ON_EPSILON 0 //0.0005 + +#define TRACEPLANE_EPSILON 0.125 + +typedef struct aas_tracestack_s +{ + vec3_t start; //start point of the piece of line to trace + vec3_t end; //end point of the piece of line to trace + int planenum; //last plane used as splitter + int nodenum; //node found after splitting with planenum +} aas_tracestack_t; + +int numaaslinks; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs) +{ + int index; + //bounding box size for each presence type + vec3_t boxmins[3] = {{0, 0, 0}, {-15, -15, -24}, {-15, -15, -24}}; + vec3_t boxmaxs[3] = {{0, 0, 0}, { 15, 15, 32}, { 15, 15, 8}}; + + if (presencetype == PRESENCE_NORMAL) index = 1; + else if (presencetype == PRESENCE_CROUCH) index = 2; + else + { + botimport.Print(PRT_FATAL, "AAS_PresenceTypeBoundingBox: unknown presence type\n"); + index = 2; + } //end if + VectorCopy(boxmins[index], mins); + VectorCopy(boxmaxs[index], maxs); +} //end of the function AAS_PresenceTypeBoundingBox +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitAASLinkHeap(void) +{ + int i, max_aaslinks; + + max_aaslinks = aasworld.linkheapsize; + //if there's no link heap present + if (!aasworld.linkheap) + { +#ifdef BSPC + max_aaslinks = 6144; +#else + max_aaslinks = (int) LibVarValue("max_aaslinks", "6144"); +#endif + if (max_aaslinks < 0) max_aaslinks = 0; + aasworld.linkheapsize = max_aaslinks; + aasworld.linkheap = (aas_link_t *) GetHunkMemory(max_aaslinks * sizeof(aas_link_t)); + } //end if + //link the links on the heap + aasworld.linkheap[0].prev_ent = NULL; + aasworld.linkheap[0].next_ent = &aasworld.linkheap[1]; + for (i = 1; i < max_aaslinks-1; i++) + { + aasworld.linkheap[i].prev_ent = &aasworld.linkheap[i - 1]; + aasworld.linkheap[i].next_ent = &aasworld.linkheap[i + 1]; + } //end for + aasworld.linkheap[max_aaslinks-1].prev_ent = &aasworld.linkheap[max_aaslinks-2]; + aasworld.linkheap[max_aaslinks-1].next_ent = NULL; + //pointer to the first free link + aasworld.freelinks = &aasworld.linkheap[0]; + // + numaaslinks = max_aaslinks; +} //end of the function AAS_InitAASLinkHeap +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeAASLinkHeap(void) +{ + if (aasworld.linkheap) FreeMemory(aasworld.linkheap); + aasworld.linkheap = NULL; + aasworld.linkheapsize = 0; +} //end of the function AAS_FreeAASLinkHeap +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_link_t *AAS_AllocAASLink(void) +{ + aas_link_t *link; + + link = aasworld.freelinks; + if (!link) + { +#ifndef BSPC + if (bot_developer) +#endif + { + botimport.Print(PRT_FATAL, "empty aas link heap\n"); + } //end if + return NULL; + } //end if + if (aasworld.freelinks) aasworld.freelinks = aasworld.freelinks->next_ent; + if (aasworld.freelinks) aasworld.freelinks->prev_ent = NULL; + numaaslinks--; + return link; +} //end of the function AAS_AllocAASLink +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DeAllocAASLink(aas_link_t *link) +{ + if (aasworld.freelinks) aasworld.freelinks->prev_ent = link; + link->prev_ent = NULL; + link->next_ent = aasworld.freelinks; + link->prev_area = NULL; + link->next_area = NULL; + aasworld.freelinks = link; + numaaslinks++; +} //end of the function AAS_DeAllocAASLink +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitAASLinkedEntities(void) +{ + if (!aasworld.loaded) return; + if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities); + aasworld.arealinkedentities = (aas_link_t **) GetClearedHunkMemory( + aasworld.numareas * sizeof(aas_link_t *)); +} //end of the function AAS_InitAASLinkedEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeAASLinkedEntities(void) +{ + if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities); + aasworld.arealinkedentities = NULL; +} //end of the function AAS_InitAASLinkedEntities +//=========================================================================== +// returns the AAS area the point is in +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PointAreaNum(vec3_t point) +{ + int nodenum; + vec_t dist; + aas_node_t *node; + aas_plane_t *plane; + + if (!aasworld.loaded) + { + botimport.Print(PRT_ERROR, "AAS_PointAreaNum: aas not loaded\n"); + return 0; + } //end if + + //start with node 1 because node zero is a dummy used for solid leafs + nodenum = 1; + while (nodenum > 0) + { +// botimport.Print(PRT_MESSAGE, "[%d]", nodenum); +#ifdef AAS_SAMPLE_DEBUG + if (nodenum >= aasworld.numnodes) + { + botimport.Print(PRT_ERROR, "nodenum = %d >= aasworld.numnodes = %d\n", nodenum, aasworld.numnodes); + return 0; + } //end if +#endif //AAS_SAMPLE_DEBUG + node = &aasworld.nodes[nodenum]; +#ifdef AAS_SAMPLE_DEBUG + if (node->planenum < 0 || node->planenum >= aasworld.numplanes) + { + botimport.Print(PRT_ERROR, "node->planenum = %d >= aasworld.numplanes = %d\n", node->planenum, aasworld.numplanes); + return 0; + } //end if +#endif //AAS_SAMPLE_DEBUG + plane = &aasworld.planes[node->planenum]; + dist = DotProduct(point, plane->normal) - plane->dist; + if (dist > 0) nodenum = node->children[0]; + else nodenum = node->children[1]; + } //end while + if (!nodenum) + { +#ifdef AAS_SAMPLE_DEBUG + botimport.Print(PRT_MESSAGE, "in solid\n"); +#endif //AAS_SAMPLE_DEBUG + return 0; + } //end if + return -nodenum; +} //end of the function AAS_PointAreaNum +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PointReachabilityAreaIndex( vec3_t origin ) +{ + int areanum, cluster, i, index; + + if (!aasworld.initialized) + return 0; + + if ( !origin ) + { + index = 0; + for (i = 0; i < aasworld.numclusters; i++) + { + index += aasworld.clusters[i].numreachabilityareas; + } //end for + return index; + } //end if + + areanum = AAS_PointAreaNum( origin ); + if ( !areanum || !AAS_AreaReachability(areanum) ) + return 0; + cluster = aasworld.areasettings[areanum].cluster; + areanum = aasworld.areasettings[areanum].clusterareanum; + if (cluster < 0) + { + cluster = aasworld.portals[-cluster].frontcluster; + areanum = aasworld.portals[-cluster].clusterareanum[0]; + } //end if + + index = 0; + for (i = 0; i < cluster; i++) + { + index += aasworld.clusters[i].numreachabilityareas; + } //end for + index += areanum; + return index; +} //end of the function AAS_PointReachabilityAreaIndex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaCluster(int areanum) +{ + if (areanum <= 0 || areanum >= aasworld.numareas) + { + botimport.Print(PRT_ERROR, "AAS_AreaCluster: invalid area number\n"); + return 0; + } //end if + return aasworld.areasettings[areanum].cluster; +} //end of the function AAS_AreaCluster +//=========================================================================== +// returns the presence types of the given area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaPresenceType(int areanum) +{ + if (!aasworld.loaded) return 0; + if (areanum <= 0 || areanum >= aasworld.numareas) + { + botimport.Print(PRT_ERROR, "AAS_AreaPresenceType: invalid area number\n"); + return 0; + } //end if + return aasworld.areasettings[areanum].presencetype; +} //end of the function AAS_AreaPresenceType +//=========================================================================== +// returns the presence type at the given point +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PointPresenceType(vec3_t point) +{ + int areanum; + + if (!aasworld.loaded) return 0; + + areanum = AAS_PointAreaNum(point); + if (!areanum) return PRESENCE_NONE; + return aasworld.areasettings[areanum].presencetype; +} //end of the function AAS_PointPresenceType +//=========================================================================== +// calculates the minimum distance between the origin of the box and the +// given plane when both will collide on the given side of the plane +// +// normal = normal vector of plane to calculate distance from +// mins = minimums of box relative to origin +// maxs = maximums of box relative to origin +// side = side of the plane we want to calculate the distance from +// 0 normal vector side +// 1 not normal vector side +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +vec_t AAS_BoxOriginDistanceFromPlane(vec3_t normal, vec3_t mins, vec3_t maxs, int side) +{ + vec3_t v1, v2; + int i; + + //swap maxs and mins when on the other side of the plane + if (side) + { + //get a point of the box that would be one of the first + //to collide with the plane + for (i = 0; i < 3; i++) + { + if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = maxs[i]; + else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = mins[i]; + else v1[i] = 0; + } //end for + } //end if + else + { + //get a point of the box that would be one of the first + //to collide with the plane + for (i = 0; i < 3; i++) + { + if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = mins[i]; + else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = maxs[i]; + else v1[i] = 0; + } //end for + } //end else + // + VectorCopy(normal, v2); + VectorInverse(v2); +// VectorNegate(normal, v2); + return DotProduct(v1, v2); +} //end of the function AAS_BoxOriginDistanceFromPlane +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_AreaEntityCollision(int areanum, vec3_t start, vec3_t end, + int presencetype, int passent, aas_trace_t *trace) +{ + int collision; + vec3_t boxmins, boxmaxs; + aas_link_t *link; + bsp_trace_t bsptrace; + + AAS_PresenceTypeBoundingBox(presencetype, boxmins, boxmaxs); + + Com_Memset(&bsptrace, 0, sizeof(bsp_trace_t)); //make compiler happy + //assume no collision + bsptrace.fraction = 1; + collision = qfalse; + for (link = aasworld.arealinkedentities[areanum]; link; link = link->next_ent) + { + //ignore the pass entity + if (link->entnum == passent) continue; + // + if (AAS_EntityCollision(link->entnum, start, boxmins, boxmaxs, end, + CONTENTS_SOLID|CONTENTS_PLAYERCLIP, &bsptrace)) + { + collision = qtrue; + } //end if + } //end for + if (collision) + { + trace->startsolid = bsptrace.startsolid; + trace->ent = bsptrace.ent; + VectorCopy(bsptrace.endpos, trace->endpos); + trace->area = 0; + trace->planenum = 0; + return qtrue; + } //end if + return qfalse; +} //end of the function AAS_AreaEntityCollision +//=========================================================================== +// recursive subdivision of the line by the BSP tree. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype, + int passent) +{ + int side, nodenum, tmpplanenum; + float front, back, frac; + vec3_t cur_start, cur_end, cur_mid, v1, v2; + aas_tracestack_t tracestack[127]; + aas_tracestack_t *tstack_p; + aas_node_t *aasnode; + aas_plane_t *plane; + aas_trace_t trace; + + //clear the trace structure + Com_Memset(&trace, 0, sizeof(aas_trace_t)); + + if (!aasworld.loaded) return trace; + + tstack_p = tracestack; + //we start with the whole line on the stack + VectorCopy(start, tstack_p->start); + VectorCopy(end, tstack_p->end); + tstack_p->planenum = 0; + //start with node 1 because node zero is a dummy for a solid leaf + tstack_p->nodenum = 1; //starting at the root of the tree + tstack_p++; + + while (1) + { + //pop up the stack + tstack_p--; + //if the trace stack is empty (ended up with a piece of the + //line to be traced in an area) + if (tstack_p < tracestack) + { + tstack_p++; + //nothing was hit + trace.startsolid = qfalse; + trace.fraction = 1.0; + //endpos is the end of the line + VectorCopy(end, trace.endpos); + //nothing hit + trace.ent = 0; + trace.area = 0; + trace.planenum = 0; + return trace; + } //end if + //number of the current node to test the line against + nodenum = tstack_p->nodenum; + //if it is an area + if (nodenum < 0) + { +#ifdef AAS_SAMPLE_DEBUG + if (-nodenum > aasworld.numareasettings) + { + botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: -nodenum out of range\n"); + return trace; + } //end if +#endif //AAS_SAMPLE_DEBUG + //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start)); + //if can't enter the area because it hasn't got the right presence type + if (!(aasworld.areasettings[-nodenum].presencetype & presencetype)) + { + //if the start point is still the initial start point + //NOTE: no need for epsilons because the points will be + //exactly the same when they're both the start point + if (tstack_p->start[0] == start[0] && + tstack_p->start[1] == start[1] && + tstack_p->start[2] == start[2]) + { + trace.startsolid = qtrue; + trace.fraction = 0.0; + VectorClear(v1); + } //end if + else + { + trace.startsolid = qfalse; + VectorSubtract(end, start, v1); + VectorSubtract(tstack_p->start, start, v2); + trace.fraction = VectorLength(v2) / VectorNormalize(v1); + VectorMA(tstack_p->start, -0.125, v1, tstack_p->start); + } //end else + VectorCopy(tstack_p->start, trace.endpos); + trace.ent = 0; + trace.area = -nodenum; +// VectorSubtract(end, start, v1); + trace.planenum = tstack_p->planenum; + //always take the plane with normal facing towards the trace start + plane = &aasworld.planes[trace.planenum]; + if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1; + return trace; + } //end if + else + { + if (passent >= 0) + { + if (AAS_AreaEntityCollision(-nodenum, tstack_p->start, + tstack_p->end, presencetype, passent, + &trace)) + { + if (!trace.startsolid) + { + VectorSubtract(end, start, v1); + VectorSubtract(trace.endpos, start, v2); + trace.fraction = VectorLength(v2) / VectorLength(v1); + } //end if + return trace; + } //end if + } //end if + } //end else + trace.lastarea = -nodenum; + continue; + } //end if + //if it is a solid leaf + if (!nodenum) + { + //if the start point is still the initial start point + //NOTE: no need for epsilons because the points will be + //exactly the same when they're both the start point + if (tstack_p->start[0] == start[0] && + tstack_p->start[1] == start[1] && + tstack_p->start[2] == start[2]) + { + trace.startsolid = qtrue; + trace.fraction = 0.0; + VectorClear(v1); + } //end if + else + { + trace.startsolid = qfalse; + VectorSubtract(end, start, v1); + VectorSubtract(tstack_p->start, start, v2); + trace.fraction = VectorLength(v2) / VectorNormalize(v1); + VectorMA(tstack_p->start, -0.125, v1, tstack_p->start); + } //end else + VectorCopy(tstack_p->start, trace.endpos); + trace.ent = 0; + trace.area = 0; //hit solid leaf +// VectorSubtract(end, start, v1); + trace.planenum = tstack_p->planenum; + //always take the plane with normal facing towards the trace start + plane = &aasworld.planes[trace.planenum]; + if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1; + return trace; + } //end if +#ifdef AAS_SAMPLE_DEBUG + if (nodenum > aasworld.numnodes) + { + botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: nodenum out of range\n"); + return trace; + } //end if +#endif //AAS_SAMPLE_DEBUG + //the node to test against + aasnode = &aasworld.nodes[nodenum]; + //start point of current line to test against node + VectorCopy(tstack_p->start, cur_start); + //end point of the current line to test against node + VectorCopy(tstack_p->end, cur_end); + //the current node plane + plane = &aasworld.planes[aasnode->planenum]; + + switch(plane->type) + {/*FIXME: wtf doesn't this work? obviously the axial node planes aren't always facing positive!!! + //check for axial planes + case PLANE_X: + { + front = cur_start[0] - plane->dist; + back = cur_end[0] - plane->dist; + break; + } //end case + case PLANE_Y: + { + front = cur_start[1] - plane->dist; + back = cur_end[1] - plane->dist; + break; + } //end case + case PLANE_Z: + { + front = cur_start[2] - plane->dist; + back = cur_end[2] - plane->dist; + break; + } //end case*/ + default: //gee it's not an axial plane + { + front = DotProduct(cur_start, plane->normal) - plane->dist; + back = DotProduct(cur_end, plane->normal) - plane->dist; + break; + } //end default + } //end switch + // bk010221 - old location of FPE hack and divide by zero expression + //if the whole to be traced line is totally at the front of this node + //only go down the tree with the front child + if ((front >= -ON_EPSILON && back >= -ON_EPSILON)) + { + //keep the current start and end point on the stack + //and go down the tree with the front child + tstack_p->nodenum = aasnode->children[0]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); + return trace; + } //end if + } //end if + //if the whole to be traced line is totally at the back of this node + //only go down the tree with the back child + else if ((front < ON_EPSILON && back < ON_EPSILON)) + { + //keep the current start and end point on the stack + //and go down the tree with the back child + tstack_p->nodenum = aasnode->children[1]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); + return trace; + } //end if + } //end if + //go down the tree both at the front and back of the node + else + { + tmpplanenum = tstack_p->planenum; + // bk010221 - new location of divide by zero (see above) + if ( front == back ) front -= 0.001f; // bk0101022 - hack/FPE + //calculate the hitpoint with the node (split point of the line) + //put the crosspoint TRACEPLANE_EPSILON pixels on the near side + if (front < 0) frac = (front + TRACEPLANE_EPSILON)/(front-back); + else frac = (front - TRACEPLANE_EPSILON)/(front-back); // bk010221 + // + if (frac < 0) + frac = 0.001f; //0 + else if (frac > 1) + frac = 0.999f; //1 + //frac = front / (front-back); + // + cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac; + cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac; + cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac; + +// AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED); + //side the front part of the line is on + side = front < 0; + //first put the end part of the line on the stack (back side) + VectorCopy(cur_mid, tstack_p->start); + //not necesary to store because still on stack + //VectorCopy(cur_end, tstack_p->end); + tstack_p->planenum = aasnode->planenum; + tstack_p->nodenum = aasnode->children[!side]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); + return trace; + } //end if + //now put the part near the start of the line on the stack so we will + //continue with thats part first. This way we'll find the first + //hit of the bbox + VectorCopy(cur_start, tstack_p->start); + VectorCopy(cur_mid, tstack_p->end); + tstack_p->planenum = tmpplanenum; + tstack_p->nodenum = aasnode->children[side]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); + return trace; + } //end if + } //end else + } //end while +// return trace; +} //end of the function AAS_TraceClientBBox +//=========================================================================== +// recursive subdivision of the line by the BSP tree. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas) +{ + int side, nodenum, tmpplanenum; + int numareas; + float front, back, frac; + vec3_t cur_start, cur_end, cur_mid; + aas_tracestack_t tracestack[127]; + aas_tracestack_t *tstack_p; + aas_node_t *aasnode; + aas_plane_t *plane; + + numareas = 0; + areas[0] = 0; + if (!aasworld.loaded) return numareas; + + tstack_p = tracestack; + //we start with the whole line on the stack + VectorCopy(start, tstack_p->start); + VectorCopy(end, tstack_p->end); + tstack_p->planenum = 0; + //start with node 1 because node zero is a dummy for a solid leaf + tstack_p->nodenum = 1; //starting at the root of the tree + tstack_p++; + + while (1) + { + //pop up the stack + tstack_p--; + //if the trace stack is empty (ended up with a piece of the + //line to be traced in an area) + if (tstack_p < tracestack) + { + return numareas; + } //end if + //number of the current node to test the line against + nodenum = tstack_p->nodenum; + //if it is an area + if (nodenum < 0) + { +#ifdef AAS_SAMPLE_DEBUG + if (-nodenum > aasworld.numareasettings) + { + botimport.Print(PRT_ERROR, "AAS_TraceAreas: -nodenum = %d out of range\n", -nodenum); + return numareas; + } //end if +#endif //AAS_SAMPLE_DEBUG + //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start)); + areas[numareas] = -nodenum; + if (points) VectorCopy(tstack_p->start, points[numareas]); + numareas++; + if (numareas >= maxareas) return numareas; + continue; + } //end if + //if it is a solid leaf + if (!nodenum) + { + continue; + } //end if +#ifdef AAS_SAMPLE_DEBUG + if (nodenum > aasworld.numnodes) + { + botimport.Print(PRT_ERROR, "AAS_TraceAreas: nodenum out of range\n"); + return numareas; + } //end if +#endif //AAS_SAMPLE_DEBUG + //the node to test against + aasnode = &aasworld.nodes[nodenum]; + //start point of current line to test against node + VectorCopy(tstack_p->start, cur_start); + //end point of the current line to test against node + VectorCopy(tstack_p->end, cur_end); + //the current node plane + plane = &aasworld.planes[aasnode->planenum]; + + switch(plane->type) + {/*FIXME: wtf doesn't this work? obviously the node planes aren't always facing positive!!! + //check for axial planes + case PLANE_X: + { + front = cur_start[0] - plane->dist; + back = cur_end[0] - plane->dist; + break; + } //end case + case PLANE_Y: + { + front = cur_start[1] - plane->dist; + back = cur_end[1] - plane->dist; + break; + } //end case + case PLANE_Z: + { + front = cur_start[2] - plane->dist; + back = cur_end[2] - plane->dist; + break; + } //end case*/ + default: //gee it's not an axial plane + { + front = DotProduct(cur_start, plane->normal) - plane->dist; + back = DotProduct(cur_end, plane->normal) - plane->dist; + break; + } //end default + } //end switch + + //if the whole to be traced line is totally at the front of this node + //only go down the tree with the front child + if (front > 0 && back > 0) + { + //keep the current start and end point on the stack + //and go down the tree with the front child + tstack_p->nodenum = aasnode->children[0]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); + return numareas; + } //end if + } //end if + //if the whole to be traced line is totally at the back of this node + //only go down the tree with the back child + else if (front <= 0 && back <= 0) + { + //keep the current start and end point on the stack + //and go down the tree with the back child + tstack_p->nodenum = aasnode->children[1]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); + return numareas; + } //end if + } //end if + //go down the tree both at the front and back of the node + else + { + tmpplanenum = tstack_p->planenum; + //calculate the hitpoint with the node (split point of the line) + //put the crosspoint TRACEPLANE_EPSILON pixels on the near side + if (front < 0) frac = (front)/(front-back); + else frac = (front)/(front-back); + if (frac < 0) frac = 0; + else if (frac > 1) frac = 1; + //frac = front / (front-back); + // + cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac; + cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac; + cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac; + +// AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED); + //side the front part of the line is on + side = front < 0; + //first put the end part of the line on the stack (back side) + VectorCopy(cur_mid, tstack_p->start); + //not necesary to store because still on stack + //VectorCopy(cur_end, tstack_p->end); + tstack_p->planenum = aasnode->planenum; + tstack_p->nodenum = aasnode->children[!side]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); + return numareas; + } //end if + //now put the part near the start of the line on the stack so we will + //continue with thats part first. This way we'll find the first + //hit of the bbox + VectorCopy(cur_start, tstack_p->start); + VectorCopy(cur_mid, tstack_p->end); + tstack_p->planenum = tmpplanenum; + tstack_p->nodenum = aasnode->children[side]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); + return numareas; + } //end if + } //end else + } //end while +// return numareas; +} //end of the function AAS_TraceAreas +//=========================================================================== +// a simple cross product +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +// void AAS_OrthogonalToVectors(vec3_t v1, vec3_t v2, vec3_t res) +#define AAS_OrthogonalToVectors(v1, v2, res) \ + (res)[0] = ((v1)[1] * (v2)[2]) - ((v1)[2] * (v2)[1]);\ + (res)[1] = ((v1)[2] * (v2)[0]) - ((v1)[0] * (v2)[2]);\ + (res)[2] = ((v1)[0] * (v2)[1]) - ((v1)[1] * (v2)[0]); +//=========================================================================== +// tests if the given point is within the face boundaries +// +// Parameter: face : face to test if the point is in it +// pnormal : normal of the plane to use for the face +// point : point to test if inside face boundaries +// Returns: qtrue if the point is within the face boundaries +// Changes Globals: - +//=========================================================================== +qboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon) +{ + int i, firstvertex, edgenum; + vec3_t v0; + vec3_t edgevec, pointvec, sepnormal; + aas_edge_t *edge; +#ifdef AAS_SAMPLE_DEBUG + int lastvertex = 0; +#endif //AAS_SAMPLE_DEBUG + + if (!aasworld.loaded) return qfalse; + + for (i = 0; i < face->numedges; i++) + { + edgenum = aasworld.edgeindex[face->firstedge + i]; + edge = &aasworld.edges[abs(edgenum)]; + //get the first vertex of the edge + firstvertex = edgenum < 0; + VectorCopy(aasworld.vertexes[edge->v[firstvertex]], v0); + //edge vector + VectorSubtract(aasworld.vertexes[edge->v[!firstvertex]], v0, edgevec); + // +#ifdef AAS_SAMPLE_DEBUG + if (lastvertex && lastvertex != edge->v[firstvertex]) + { + botimport.Print(PRT_MESSAGE, "winding not counter clockwise\n"); + } //end if + lastvertex = edge->v[!firstvertex]; +#endif //AAS_SAMPLE_DEBUG + //vector from first edge point to point possible in face + VectorSubtract(point, v0, pointvec); + //get a vector pointing inside the face orthogonal to both the + //edge vector and the normal vector of the plane the face is in + //this vector defines a plane through the origin (first vertex of + //edge) and through both the edge vector and the normal vector + //of the plane + AAS_OrthogonalToVectors(edgevec, pnormal, sepnormal); + //check on wich side of the above plane the point is + //this is done by checking the sign of the dot product of the + //vector orthogonal vector from above and the vector from the + //origin (first vertex of edge) to the point + //if the dotproduct is smaller than zero the point is outside the face + if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse; + } //end for + return qtrue; +} //end of the function AAS_InsideFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon) +{ + int i, firstvertex, edgenum; + vec_t *v1, *v2; + vec3_t edgevec, pointvec, sepnormal; + aas_edge_t *edge; + aas_plane_t *plane; + aas_face_t *face; + + if (!aasworld.loaded) return qfalse; + + face = &aasworld.faces[facenum]; + plane = &aasworld.planes[face->planenum]; + // + for (i = 0; i < face->numedges; i++) + { + edgenum = aasworld.edgeindex[face->firstedge + i]; + edge = &aasworld.edges[abs(edgenum)]; + //get the first vertex of the edge + firstvertex = edgenum < 0; + v1 = aasworld.vertexes[edge->v[firstvertex]]; + v2 = aasworld.vertexes[edge->v[!firstvertex]]; + //edge vector + VectorSubtract(v2, v1, edgevec); + //vector from first edge point to point possible in face + VectorSubtract(point, v1, pointvec); + // + CrossProduct(edgevec, plane->normal, sepnormal); + // + if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse; + } //end for + return qtrue; +} //end of the function AAS_PointInsideFace +//=========================================================================== +// returns the ground face the given point is above in the given area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point) +{ + int i, facenum; + vec3_t up = {0, 0, 1}; + vec3_t normal; + aas_area_t *area; + aas_face_t *face; + + if (!aasworld.loaded) return NULL; + + area = &aasworld.areas[areanum]; + for (i = 0; i < area->numfaces; i++) + { + facenum = aasworld.faceindex[area->firstface + i]; + face = &aasworld.faces[abs(facenum)]; + //if this is a ground face + if (face->faceflags & FACE_GROUND) + { + //get the up or down normal + if (aasworld.planes[face->planenum].normal[2] < 0) VectorNegate(up, normal); + else VectorCopy(up, normal); + //check if the point is in the face + if (AAS_InsideFace(face, normal, point, 0.01f)) return face; + } //end if + } //end for + return NULL; +} //end of the function AAS_AreaGroundFace +//=========================================================================== +// returns the face the trace end position is situated in +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FacePlane(int facenum, vec3_t normal, float *dist) +{ + aas_plane_t *plane; + + plane = &aasworld.planes[aasworld.faces[facenum].planenum]; + VectorCopy(plane->normal, normal); + *dist = plane->dist; +} //end of the function AAS_FacePlane +//=========================================================================== +// returns the face the trace end position is situated in +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_face_t *AAS_TraceEndFace(aas_trace_t *trace) +{ + int i, facenum; + aas_area_t *area; + aas_face_t *face, *firstface = NULL; + + if (!aasworld.loaded) return NULL; + + //if started in solid no face was hit + if (trace->startsolid) return NULL; + //trace->lastarea is the last area the trace was in + area = &aasworld.areas[trace->lastarea]; + //check which face the trace.endpos was in + for (i = 0; i < area->numfaces; i++) + { + facenum = aasworld.faceindex[area->firstface + i]; + face = &aasworld.faces[abs(facenum)]; + //if the face is in the same plane as the trace end point + if ((face->planenum & ~1) == (trace->planenum & ~1)) + { + //firstface is used for optimization, if theres only one + //face in the plane then it has to be the good one + //if there are more faces in the same plane then always + //check the one with the fewest edges first +/* if (firstface) + { + if (firstface->numedges < face->numedges) + { + if (AAS_InsideFace(firstface, + aasworld.planes[face->planenum].normal, trace->endpos)) + { + return firstface; + } //end if + firstface = face; + } //end if + else + { + if (AAS_InsideFace(face, + aasworld.planes[face->planenum].normal, trace->endpos)) + { + return face; + } //end if + } //end else + } //end if + else + { + firstface = face; + } //end else*/ + if (AAS_InsideFace(face, + aasworld.planes[face->planenum].normal, trace->endpos, 0.01f)) return face; + } //end if + } //end for + return firstface; +} //end of the function AAS_TraceEndFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BoxOnPlaneSide2(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p) +{ + int i, sides; + float dist1, dist2; + vec3_t corners[2]; + + for (i = 0; i < 3; i++) + { + if (p->normal[i] < 0) + { + corners[0][i] = absmins[i]; + corners[1][i] = absmaxs[i]; + } //end if + else + { + corners[1][i] = absmins[i]; + corners[0][i] = absmaxs[i]; + } //end else + } //end for + dist1 = DotProduct(p->normal, corners[0]) - p->dist; + dist2 = DotProduct(p->normal, corners[1]) - p->dist; + sides = 0; + if (dist1 >= 0) sides = 1; + if (dist2 < 0) sides |= 2; + + return sides; +} //end of the function AAS_BoxOnPlaneSide2 +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +//int AAS_BoxOnPlaneSide(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p) +#define AAS_BoxOnPlaneSide(absmins, absmaxs, p) (\ + ( (p)->type < 3) ?\ + (\ + ( (p)->dist <= (absmins)[(p)->type]) ?\ + (\ + 1\ + )\ + :\ + (\ + ( (p)->dist >= (absmaxs)[(p)->type]) ?\ + (\ + 2\ + )\ + :\ + (\ + 3\ + )\ + )\ + )\ + :\ + (\ + AAS_BoxOnPlaneSide2((absmins), (absmaxs), (p))\ + )\ +) //end of the function AAS_BoxOnPlaneSide +//=========================================================================== +// remove the links to this entity from all areas +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_UnlinkFromAreas(aas_link_t *areas) +{ + aas_link_t *link, *nextlink; + + for (link = areas; link; link = nextlink) + { + //next area the entity is linked in + nextlink = link->next_area; + //remove the entity from the linked list of this area + if (link->prev_ent) link->prev_ent->next_ent = link->next_ent; + else aasworld.arealinkedentities[link->areanum] = link->next_ent; + if (link->next_ent) link->next_ent->prev_ent = link->prev_ent; + //deallocate the link structure + AAS_DeAllocAASLink(link); + } //end for +} //end of the function AAS_UnlinkFromAreas +//=========================================================================== +// link the entity to the areas the bounding box is totally or partly +// situated in. This is done with recursion down the tree using the +// bounding box to test for plane sides +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +typedef struct +{ + int nodenum; //node found after splitting +} aas_linkstack_t; + +aas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum) +{ + int side, nodenum; + aas_linkstack_t linkstack[128]; + aas_linkstack_t *lstack_p; + aas_node_t *aasnode; + aas_plane_t *plane; + aas_link_t *link, *areas; + + if (!aasworld.loaded) + { + botimport.Print(PRT_ERROR, "AAS_LinkEntity: aas not loaded\n"); + return NULL; + } //end if + + areas = NULL; + // + lstack_p = linkstack; + //we start with the whole line on the stack + //start with node 1 because node zero is a dummy used for solid leafs + lstack_p->nodenum = 1; //starting at the root of the tree + lstack_p++; + + while (1) + { + //pop up the stack + lstack_p--; + //if the trace stack is empty (ended up with a piece of the + //line to be traced in an area) + if (lstack_p < linkstack) break; + //number of the current node to test the line against + nodenum = lstack_p->nodenum; + //if it is an area + if (nodenum < 0) + { + //NOTE: the entity might have already been linked into this area + // because several node children can point to the same area + for (link = aasworld.arealinkedentities[-nodenum]; link; link = link->next_ent) + { + if (link->entnum == entnum) break; + } //end for + if (link) continue; + // + link = AAS_AllocAASLink(); + if (!link) return areas; + link->entnum = entnum; + link->areanum = -nodenum; + //put the link into the double linked area list of the entity + link->prev_area = NULL; + link->next_area = areas; + if (areas) areas->prev_area = link; + areas = link; + //put the link into the double linked entity list of the area + link->prev_ent = NULL; + link->next_ent = aasworld.arealinkedentities[-nodenum]; + if (aasworld.arealinkedentities[-nodenum]) + aasworld.arealinkedentities[-nodenum]->prev_ent = link; + aasworld.arealinkedentities[-nodenum] = link; + // + continue; + } //end if + //if solid leaf + if (!nodenum) continue; + //the node to test against + aasnode = &aasworld.nodes[nodenum]; + //the current node plane + plane = &aasworld.planes[aasnode->planenum]; + //get the side(s) the box is situated relative to the plane + side = AAS_BoxOnPlaneSide2(absmins, absmaxs, plane); + //if on the front side of the node + if (side & 1) + { + lstack_p->nodenum = aasnode->children[0]; + lstack_p++; + } //end if + if (lstack_p >= &linkstack[127]) + { + botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n"); + break; + } //end if + //if on the back side of the node + if (side & 2) + { + lstack_p->nodenum = aasnode->children[1]; + lstack_p++; + } //end if + if (lstack_p >= &linkstack[127]) + { + botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n"); + break; + } //end if + } //end while + return areas; +} //end of the function AAS_AASLinkEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_link_t *AAS_LinkEntityClientBBox(vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype) +{ + vec3_t mins, maxs; + vec3_t newabsmins, newabsmaxs; + + AAS_PresenceTypeBoundingBox(presencetype, mins, maxs); + VectorSubtract(absmins, maxs, newabsmins); + VectorSubtract(absmaxs, mins, newabsmaxs); + //relink the entity + return AAS_AASLinkEntity(newabsmins, newabsmaxs, entnum); +} //end of the function AAS_LinkEntityClientBBox +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas) +{ + aas_link_t *linkedareas, *link; + int num; + + linkedareas = AAS_AASLinkEntity(absmins, absmaxs, -1); + num = 0; + for (link = linkedareas; link; link = link->next_area) + { + areas[num] = link->areanum; + num++; + if (num >= maxareas) + break; + } //end for + AAS_UnlinkFromAreas(linkedareas); + return num; +} //end of the function AAS_BBoxAreas +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaInfo( int areanum, aas_areainfo_t *info ) +{ + aas_areasettings_t *settings; + if (!info) + return 0; + if (areanum <= 0 || areanum >= aasworld.numareas) + { + botimport.Print(PRT_ERROR, "AAS_AreaInfo: areanum %d out of range\n", areanum); + return 0; + } //end if + settings = &aasworld.areasettings[areanum]; + info->cluster = settings->cluster; + info->contents = settings->contents; + info->flags = settings->areaflags; + info->presencetype = settings->presencetype; + VectorCopy(aasworld.areas[areanum].mins, info->mins); + VectorCopy(aasworld.areas[areanum].maxs, info->maxs); + VectorCopy(aasworld.areas[areanum].center, info->center); + return sizeof(aas_areainfo_t); +} //end of the function AAS_AreaInfo +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_plane_t *AAS_PlaneFromNum(int planenum) +{ + if (!aasworld.loaded) return NULL; + + return &aasworld.planes[planenum]; +} //end of the function AAS_PlaneFromNum diff --git a/reaction/engine/code/botlib/be_aas_sample.h b/reaction/engine/code/botlib/be_aas_sample.h new file mode 100644 index 00000000..ed6c2371 --- /dev/null +++ b/reaction/engine/code/botlib/be_aas_sample.h @@ -0,0 +1,69 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_sample.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_sample.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +void AAS_InitAASLinkHeap(void); +void AAS_InitAASLinkedEntities(void); +void AAS_FreeAASLinkHeap(void); +void AAS_FreeAASLinkedEntities(void); +aas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point); +aas_face_t *AAS_TraceEndFace(aas_trace_t *trace); +aas_plane_t *AAS_PlaneFromNum(int planenum); +aas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum); +aas_link_t *AAS_LinkEntityClientBBox(vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype); +qboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon); +qboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon); +void AAS_UnlinkFromAreas(aas_link_t *areas); +#endif //AASINTERN + +//returns the mins and maxs of the bounding box for the given presence type +void AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs); +//returns the cluster the area is in (negative portal number if the area is a portal) +int AAS_AreaCluster(int areanum); +//returns the presence type(s) of the area +int AAS_AreaPresenceType(int areanum); +//returns the presence type(s) at the given point +int AAS_PointPresenceType(vec3_t point); +//returns the result of the trace of a client bbox +aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype, int passent); +//stores the areas the trace went through and returns the number of passed areas +int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas); +//returns the areas the bounding box is in +int AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas); +//return area information +int AAS_AreaInfo( int areanum, aas_areainfo_t *info ); +//returns the area the point is in +int AAS_PointAreaNum(vec3_t point); +// +int AAS_PointReachabilityAreaIndex( vec3_t point ); +//returns the plane the given face is in +void AAS_FacePlane(int facenum, vec3_t normal, float *dist); + diff --git a/reaction/engine/code/botlib/be_ai_char.c b/reaction/engine/code/botlib/be_ai_char.c new file mode 100644 index 00000000..51520f71 --- /dev/null +++ b/reaction/engine/code/botlib/be_ai_char.c @@ -0,0 +1,790 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_char.c + * + * desc: bot characters + * + * $Archive: /MissionPack/code/botlib/be_ai_char.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_log.h" +#include "l_memory.h" +#include "l_utils.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_libvar.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_ai_char.h" + +#define MAX_CHARACTERISTICS 80 + +#define CT_INTEGER 1 +#define CT_FLOAT 2 +#define CT_STRING 3 + +#define DEFAULT_CHARACTER "bots/default_c.c" + +//characteristic value +union cvalue +{ + int integer; + float _float; + char *string; +}; +//a characteristic +typedef struct bot_characteristic_s +{ + char type; //characteristic type + union cvalue value; //characteristic value +} bot_characteristic_t; + +//a bot character +typedef struct bot_character_s +{ + char filename[MAX_QPATH]; + float skill; + bot_characteristic_t c[1]; //variable sized +} bot_character_t; + +bot_character_t *botcharacters[MAX_CLIENTS + 1]; + +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +bot_character_t *BotCharacterFromHandle(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle); + return NULL; + } //end if + if (!botcharacters[handle]) + { + botimport.Print(PRT_FATAL, "invalid character %d\n", handle); + return NULL; + } //end if + return botcharacters[handle]; +} //end of the function BotCharacterFromHandle +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpCharacter(bot_character_t *ch) +{ + int i; + + Log_Write("%s", ch->filename); + Log_Write("skill %d\n", ch->skill); + Log_Write("{\n"); + for (i = 0; i < MAX_CHARACTERISTICS; i++) + { + switch(ch->c[i].type) + { + case CT_INTEGER: Log_Write(" %4d %d\n", i, ch->c[i].value.integer); break; + case CT_FLOAT: Log_Write(" %4d %f\n", i, ch->c[i].value._float); break; + case CT_STRING: Log_Write(" %4d %s\n", i, ch->c[i].value.string); break; + } //end case + } //end for + Log_Write("}\n"); +} //end of the function BotDumpCharacter +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeCharacterStrings(bot_character_t *ch) +{ + int i; + + for (i = 0; i < MAX_CHARACTERISTICS; i++) + { + if (ch->c[i].type == CT_STRING) + { + FreeMemory(ch->c[i].value.string); + } //end if + } //end for +} //end of the function BotFreeCharacterStrings +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeCharacter2(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle); + return; + } //end if + if (!botcharacters[handle]) + { + botimport.Print(PRT_FATAL, "invalid character %d\n", handle); + return; + } //end if + BotFreeCharacterStrings(botcharacters[handle]); + FreeMemory(botcharacters[handle]); + botcharacters[handle] = NULL; +} //end of the function BotFreeCharacter2 +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeCharacter(int handle) +{ + if (!LibVarGetValue("bot_reloadcharacters")) return; + BotFreeCharacter2(handle); +} //end of the function BotFreeCharacter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDefaultCharacteristics(bot_character_t *ch, bot_character_t *defaultch) +{ + int i; + + for (i = 0; i < MAX_CHARACTERISTICS; i++) + { + if (ch->c[i].type) continue; + // + if (defaultch->c[i].type == CT_FLOAT) + { + ch->c[i].type = CT_FLOAT; + ch->c[i].value._float = defaultch->c[i].value._float; + } //end if + else if (defaultch->c[i].type == CT_INTEGER) + { + ch->c[i].type = CT_INTEGER; + ch->c[i].value.integer = defaultch->c[i].value.integer; + } //end else if + else if (defaultch->c[i].type == CT_STRING) + { + ch->c[i].type = CT_STRING; + ch->c[i].value.string = (char *) GetMemory(strlen(defaultch->c[i].value.string)+1); + strcpy(ch->c[i].value.string, defaultch->c[i].value.string); + } //end else if + } //end for +} //end of the function BotDefaultCharacteristics +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_character_t *BotLoadCharacterFromFile(char *charfile, int skill) +{ + int indent, index, foundcharacter; + bot_character_t *ch; + source_t *source; + token_t token; + + foundcharacter = qfalse; + //a bot character is parsed in two phases + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(charfile); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", charfile); + return NULL; + } //end if + ch = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) + + MAX_CHARACTERISTICS * sizeof(bot_characteristic_t)); + strcpy(ch->filename, charfile); + while(PC_ReadToken(source, &token)) + { + if (!strcmp(token.string, "skill")) + { + if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) + { + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + if (!PC_ExpectTokenString(source, "{")) + { + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + //if it's the correct skill + if (skill < 0 || token.intvalue == skill) + { + foundcharacter = qtrue; + ch->skill = token.intvalue; + while(PC_ExpectAnyToken(source, &token)) + { + if (!strcmp(token.string, "}")) break; + if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER)) + { + SourceError(source, "expected integer index, found %s\n", token.string); + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + index = token.intvalue; + if (index < 0 || index > MAX_CHARACTERISTICS) + { + SourceError(source, "characteristic index out of range [0, %d]\n", MAX_CHARACTERISTICS); + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + if (ch->c[index].type) + { + SourceError(source, "characteristic %d already initialized\n", index); + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + if (!PC_ExpectAnyToken(source, &token)) + { + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + if (token.type == TT_NUMBER) + { + if (token.subtype & TT_FLOAT) + { + ch->c[index].value._float = token.floatvalue; + ch->c[index].type = CT_FLOAT; + } //end if + else + { + ch->c[index].value.integer = token.intvalue; + ch->c[index].type = CT_INTEGER; + } //end else + } //end if + else if (token.type == TT_STRING) + { + StripDoubleQuotes(token.string); + ch->c[index].value.string = GetMemory(strlen(token.string)+1); + strcpy(ch->c[index].value.string, token.string); + ch->c[index].type = CT_STRING; + } //end else if + else + { + SourceError(source, "expected integer, float or string, found %s\n", token.string); + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end else + } //end if + break; + } //end if + else + { + indent = 1; + while(indent) + { + if (!PC_ExpectAnyToken(source, &token)) + { + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + if (!strcmp(token.string, "{")) indent++; + else if (!strcmp(token.string, "}")) indent--; + } //end while + } //end else + } //end if + else + { + SourceError(source, "unknown definition %s\n", token.string); + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end else + } //end while + FreeSource(source); + // + if (!foundcharacter) + { + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + return ch; +} //end of the function BotLoadCharacterFromFile +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotFindCachedCharacter(char *charfile, float skill) +{ + int handle; + + for (handle = 1; handle <= MAX_CLIENTS; handle++) + { + if ( !botcharacters[handle] ) continue; + if ( strcmp( botcharacters[handle]->filename, charfile ) == 0 && + (skill < 0 || fabs(botcharacters[handle]->skill - skill) < 0.01) ) + { + return handle; + } //end if + } //end for + return 0; +} //end of the function BotFindCachedCharacter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadCachedCharacter(char *charfile, float skill, int reload) +{ + int handle, cachedhandle, intskill; + bot_character_t *ch = NULL; +#ifdef DEBUG + int starttime; + + starttime = Sys_MilliSeconds(); +#endif //DEBUG + + //find a free spot for a character + for (handle = 1; handle <= MAX_CLIENTS; handle++) + { + if (!botcharacters[handle]) break; + } //end for + if (handle > MAX_CLIENTS) return 0; + //try to load a cached character with the given skill + if (!reload) + { + cachedhandle = BotFindCachedCharacter(charfile, skill); + if (cachedhandle) + { + botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile); + return cachedhandle; + } //end if + } //end else + // + intskill = (int) (skill + 0.5); + //try to load the character with the given skill + ch = BotLoadCharacterFromFile(charfile, intskill); + if (ch) + { + botcharacters[handle] = ch; + // + botimport.Print(PRT_MESSAGE, "loaded skill %d from %s\n", intskill, charfile); +#ifdef DEBUG + if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "skill %d loaded in %d msec from %s\n", intskill, Sys_MilliSeconds() - starttime, charfile); + } //end if +#endif //DEBUG + return handle; + } //end if + // + botimport.Print(PRT_WARNING, "couldn't find skill %d in %s\n", intskill, charfile); + // + if (!reload) + { + //try to load a cached default character with the given skill + cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, skill); + if (cachedhandle) + { + botimport.Print(PRT_MESSAGE, "loaded cached default skill %d from %s\n", intskill, charfile); + return cachedhandle; + } //end if + } //end if + //try to load the default character with the given skill + ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, intskill); + if (ch) + { + botcharacters[handle] = ch; + botimport.Print(PRT_MESSAGE, "loaded default skill %d from %s\n", intskill, charfile); + return handle; + } //end if + // + if (!reload) + { + //try to load a cached character with any skill + cachedhandle = BotFindCachedCharacter(charfile, -1); + if (cachedhandle) + { + botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile); + return cachedhandle; + } //end if + } //end if + //try to load a character with any skill + ch = BotLoadCharacterFromFile(charfile, -1); + if (ch) + { + botcharacters[handle] = ch; + botimport.Print(PRT_MESSAGE, "loaded skill %f from %s\n", ch->skill, charfile); + return handle; + } //end if + // + if (!reload) + { + //try to load a cached character with any skill + cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, -1); + if (cachedhandle) + { + botimport.Print(PRT_MESSAGE, "loaded cached default skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile); + return cachedhandle; + } //end if + } //end if + //try to load a character with any skill + ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, -1); + if (ch) + { + botcharacters[handle] = ch; + botimport.Print(PRT_MESSAGE, "loaded default skill %f from %s\n", ch->skill, charfile); + return handle; + } //end if + // + botimport.Print(PRT_WARNING, "couldn't load any skill from %s\n", charfile); + //couldn't load any character + return 0; +} //end of the function BotLoadCachedCharacter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadCharacterSkill(char *charfile, float skill) +{ + int ch, defaultch; + + defaultch = BotLoadCachedCharacter(DEFAULT_CHARACTER, skill, qfalse); + ch = BotLoadCachedCharacter(charfile, skill, LibVarGetValue("bot_reloadcharacters")); + + if (defaultch && ch) + { + BotDefaultCharacteristics(botcharacters[ch], botcharacters[defaultch]); + } //end if + + return ch; +} //end of the function BotLoadCharacterSkill +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotInterpolateCharacters(int handle1, int handle2, float desiredskill) +{ + bot_character_t *ch1, *ch2, *out; + int i, handle; + float scale; + + ch1 = BotCharacterFromHandle(handle1); + ch2 = BotCharacterFromHandle(handle2); + if (!ch1 || !ch2) + return 0; + //find a free spot for a character + for (handle = 1; handle <= MAX_CLIENTS; handle++) + { + if (!botcharacters[handle]) break; + } //end for + if (handle > MAX_CLIENTS) return 0; + out = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) + + MAX_CHARACTERISTICS * sizeof(bot_characteristic_t)); + out->skill = desiredskill; + strcpy(out->filename, ch1->filename); + botcharacters[handle] = out; + + scale = (float) (desiredskill - ch1->skill) / (ch2->skill - ch1->skill); + for (i = 0; i < MAX_CHARACTERISTICS; i++) + { + // + if (ch1->c[i].type == CT_FLOAT && ch2->c[i].type == CT_FLOAT) + { + out->c[i].type = CT_FLOAT; + out->c[i].value._float = ch1->c[i].value._float + + (ch2->c[i].value._float - ch1->c[i].value._float) * scale; + } //end if + else if (ch1->c[i].type == CT_INTEGER) + { + out->c[i].type = CT_INTEGER; + out->c[i].value.integer = ch1->c[i].value.integer; + } //end else if + else if (ch1->c[i].type == CT_STRING) + { + out->c[i].type = CT_STRING; + out->c[i].value.string = (char *) GetMemory(strlen(ch1->c[i].value.string)+1); + strcpy(out->c[i].value.string, ch1->c[i].value.string); + } //end else if + } //end for + return handle; +} //end of the function BotInterpolateCharacters +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadCharacter(char *charfile, float skill) +{ + int firstskill, secondskill, handle; + + //make sure the skill is in the valid range + if (skill < 1.0) skill = 1.0; + else if (skill > 5.0) skill = 5.0; + //skill 1, 4 and 5 should be available in the character files + if (skill == 1.0 || skill == 4.0 || skill == 5.0) + { + return BotLoadCharacterSkill(charfile, skill); + } //end if + //check if there's a cached skill + handle = BotFindCachedCharacter(charfile, skill); + if (handle) + { + botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile); + return handle; + } //end if + if (skill < 4.0) + { + //load skill 1 and 4 + firstskill = BotLoadCharacterSkill(charfile, 1); + if (!firstskill) return 0; + secondskill = BotLoadCharacterSkill(charfile, 4); + if (!secondskill) return firstskill; + } //end if + else + { + //load skill 4 and 5 + firstskill = BotLoadCharacterSkill(charfile, 4); + if (!firstskill) return 0; + secondskill = BotLoadCharacterSkill(charfile, 5); + if (!secondskill) return firstskill; + } //end else + //interpolate between the two skills + handle = BotInterpolateCharacters(firstskill, secondskill, skill); + if (!handle) return 0; + //write the character to the log file + BotDumpCharacter(botcharacters[handle]); + // + return handle; +} //end of the function BotLoadCharacter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int CheckCharacteristicIndex(int character, int index) +{ + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return qfalse; + if (index < 0 || index >= MAX_CHARACTERISTICS) + { + botimport.Print(PRT_ERROR, "characteristic %d does not exist\n", index); + return qfalse; + } //end if + if (!ch->c[index].type) + { + botimport.Print(PRT_ERROR, "characteristic %d is not initialized\n", index); + return qfalse; + } //end if + return qtrue; +} //end of the function CheckCharacteristicIndex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float Characteristic_Float(int character, int index) +{ + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return 0; + //check if the index is in range + if (!CheckCharacteristicIndex(character, index)) return 0; + //an integer will be converted to a float + if (ch->c[index].type == CT_INTEGER) + { + return (float) ch->c[index].value.integer; + } //end if + //floats are just returned + else if (ch->c[index].type == CT_FLOAT) + { + return ch->c[index].value._float; + } //end else if + //cannot convert a string pointer to a float + else + { + botimport.Print(PRT_ERROR, "characteristic %d is not a float\n", index); + return 0; + } //end else if +// return 0; +} //end of the function Characteristic_Float +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float Characteristic_BFloat(int character, int index, float min, float max) +{ + float value; + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return 0; + if (min > max) + { + botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %f and %f\n", index, min, max); + return 0; + } //end if + value = Characteristic_Float(character, index); + if (value < min) return min; + if (value > max) return max; + return value; +} //end of the function Characteristic_BFloat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Characteristic_Integer(int character, int index) +{ + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return 0; + //check if the index is in range + if (!CheckCharacteristicIndex(character, index)) return 0; + //an integer will just be returned + if (ch->c[index].type == CT_INTEGER) + { + return ch->c[index].value.integer; + } //end if + //floats are casted to integers + else if (ch->c[index].type == CT_FLOAT) + { + return (int) ch->c[index].value._float; + } //end else if + else + { + botimport.Print(PRT_ERROR, "characteristic %d is not a integer\n", index); + return 0; + } //end else if +// return 0; +} //end of the function Characteristic_Integer +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Characteristic_BInteger(int character, int index, int min, int max) +{ + int value; + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return 0; + if (min > max) + { + botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %d and %d\n", index, min, max); + return 0; + } //end if + value = Characteristic_Integer(character, index); + if (value < min) return min; + if (value > max) return max; + return value; +} //end of the function Characteristic_BInteger +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Characteristic_String(int character, int index, char *buf, int size) +{ + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return; + //check if the index is in range + if (!CheckCharacteristicIndex(character, index)) return; + //an integer will be converted to a float + if (ch->c[index].type == CT_STRING) + { + strncpy(buf, ch->c[index].value.string, size-1); + buf[size-1] = '\0'; + return; + } //end if + else + { + botimport.Print(PRT_ERROR, "characteristic %d is not a string\n", index); + return; + } //end else if + return; +} //end of the function Characteristic_String +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotShutdownCharacters(void) +{ + int handle; + + for (handle = 1; handle <= MAX_CLIENTS; handle++) + { + if (botcharacters[handle]) + { + BotFreeCharacter2(handle); + } //end if + } //end for +} //end of the function BotShutdownCharacters + diff --git a/reaction/engine/code/botlib/be_ai_char.h b/reaction/engine/code/botlib/be_ai_char.h new file mode 100644 index 00000000..719d68f6 --- /dev/null +++ b/reaction/engine/code/botlib/be_ai_char.h @@ -0,0 +1,48 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: be_ai_char.h + * + * desc: bot characters + * + * $Archive: /source/code/botlib/be_ai_char.h $ + * + *****************************************************************************/ + +//loads a bot character from a file +int BotLoadCharacter(char *charfile, float skill); +//frees a bot character +void BotFreeCharacter(int character); +//returns a float characteristic +float Characteristic_Float(int character, int index); +//returns a bounded float characteristic +float Characteristic_BFloat(int character, int index, float min, float max); +//returns an integer characteristic +int Characteristic_Integer(int character, int index); +//returns a bounded integer characteristic +int Characteristic_BInteger(int character, int index, int min, int max); +//returns a string characteristic +void Characteristic_String(int character, int index, char *buf, int size); +//free cached bot characters +void BotShutdownCharacters(void); diff --git a/reaction/engine/code/botlib/be_ai_chat.c b/reaction/engine/code/botlib/be_ai_chat.c new file mode 100644 index 00000000..6d64cf63 --- /dev/null +++ b/reaction/engine/code/botlib/be_ai_chat.c @@ -0,0 +1,3042 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_chat.c + * + * desc: bot chat AI + * + * $Archive: /MissionPack/code/botlib/be_ai_chat.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_libvar.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_utils.h" +#include "l_log.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_ea.h" +#include "be_ai_chat.h" + + +//escape character +#define ESCAPE_CHAR 0x01 //'_' +// +// "hi ", people, " ", 0, " entered the game" +//becomes: +// "hi _rpeople_ _v0_ entered the game" +// + +//match piece types +#define MT_VARIABLE 1 //variable match piece +#define MT_STRING 2 //string match piece +//reply chat key flags +#define RCKFL_AND 1 //key must be present +#define RCKFL_NOT 2 //key must be absent +#define RCKFL_NAME 4 //name of bot must be present +#define RCKFL_STRING 8 //key is a string +#define RCKFL_VARIABLES 16 //key is a match template +#define RCKFL_BOTNAMES 32 //key is a series of botnames +#define RCKFL_GENDERFEMALE 64 //bot must be female +#define RCKFL_GENDERMALE 128 //bot must be male +#define RCKFL_GENDERLESS 256 //bot must be genderless +//time to ignore a chat message after using it +#define CHATMESSAGE_RECENTTIME 20 + +//the actuall chat messages +typedef struct bot_chatmessage_s +{ + char *chatmessage; //chat message string + float time; //last time used + struct bot_chatmessage_s *next; //next chat message in a list +} bot_chatmessage_t; +//bot chat type with chat lines +typedef struct bot_chattype_s +{ + char name[MAX_CHATTYPE_NAME]; + int numchatmessages; + bot_chatmessage_t *firstchatmessage; + struct bot_chattype_s *next; +} bot_chattype_t; +//bot chat lines +typedef struct bot_chat_s +{ + bot_chattype_t *types; +} bot_chat_t; + +//random string +typedef struct bot_randomstring_s +{ + char *string; + struct bot_randomstring_s *next; +} bot_randomstring_t; +//list with random strings +typedef struct bot_randomlist_s +{ + char *string; + int numstrings; + bot_randomstring_t *firstrandomstring; + struct bot_randomlist_s *next; +} bot_randomlist_t; + +//synonym +typedef struct bot_synonym_s +{ + char *string; + float weight; + struct bot_synonym_s *next; +} bot_synonym_t; +//list with synonyms +typedef struct bot_synonymlist_s +{ + unsigned long int context; + float totalweight; + bot_synonym_t *firstsynonym; + struct bot_synonymlist_s *next; +} bot_synonymlist_t; + +//fixed match string +typedef struct bot_matchstring_s +{ + char *string; + struct bot_matchstring_s *next; +} bot_matchstring_t; + +//piece of a match template +typedef struct bot_matchpiece_s +{ + int type; + bot_matchstring_t *firststring; + int variable; + struct bot_matchpiece_s *next; +} bot_matchpiece_t; +//match template +typedef struct bot_matchtemplate_s +{ + unsigned long int context; + int type; + int subtype; + bot_matchpiece_t *first; + struct bot_matchtemplate_s *next; +} bot_matchtemplate_t; + +//reply chat key +typedef struct bot_replychatkey_s +{ + int flags; + char *string; + bot_matchpiece_t *match; + struct bot_replychatkey_s *next; +} bot_replychatkey_t; +//reply chat +typedef struct bot_replychat_s +{ + bot_replychatkey_t *keys; + float priority; + int numchatmessages; + bot_chatmessage_t *firstchatmessage; + struct bot_replychat_s *next; +} bot_replychat_t; + +//string list +typedef struct bot_stringlist_s +{ + char *string; + struct bot_stringlist_s *next; +} bot_stringlist_t; + +//chat state of a bot +typedef struct bot_chatstate_s +{ + int gender; //0=it, 1=female, 2=male + int client; //client number + char name[32]; //name of the bot + char chatmessage[MAX_MESSAGE_SIZE]; + int handle; + //the console messages visible to the bot + bot_consolemessage_t *firstmessage; //first message is the first typed message + bot_consolemessage_t *lastmessage; //last message is the last typed message, bottom of console + //number of console messages stored in the state + int numconsolemessages; + //the bot chat lines + bot_chat_t *chat; +} bot_chatstate_t; + +typedef struct { + bot_chat_t *chat; + char filename[MAX_QPATH]; + char chatname[MAX_QPATH]; +} bot_ichatdata_t; + +bot_ichatdata_t *ichatdata[MAX_CLIENTS]; + +bot_chatstate_t *botchatstates[MAX_CLIENTS+1]; +//console message heap +bot_consolemessage_t *consolemessageheap = NULL; +bot_consolemessage_t *freeconsolemessages = NULL; +//list with match strings +bot_matchtemplate_t *matchtemplates = NULL; +//list with synonyms +bot_synonymlist_t *synonyms = NULL; +//list with random strings +bot_randomlist_t *randomstrings = NULL; +//reply chats +bot_replychat_t *replychats = NULL; + +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +bot_chatstate_t *BotChatStateFromHandle(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "chat state handle %d out of range\n", handle); + return NULL; + } //end if + if (!botchatstates[handle]) + { + botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle); + return NULL; + } //end if + return botchatstates[handle]; +} //end of the function BotChatStateFromHandle +//=========================================================================== +// initialize the heap with unused console messages +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void InitConsoleMessageHeap(void) +{ + int i, max_messages; + + if (consolemessageheap) FreeMemory(consolemessageheap); + // + max_messages = (int) LibVarValue("max_messages", "1024"); + consolemessageheap = (bot_consolemessage_t *) GetClearedHunkMemory(max_messages * + sizeof(bot_consolemessage_t)); + consolemessageheap[0].prev = NULL; + consolemessageheap[0].next = &consolemessageheap[1]; + for (i = 1; i < max_messages-1; i++) + { + consolemessageheap[i].prev = &consolemessageheap[i - 1]; + consolemessageheap[i].next = &consolemessageheap[i + 1]; + } //end for + consolemessageheap[max_messages-1].prev = &consolemessageheap[max_messages-2]; + consolemessageheap[max_messages-1].next = NULL; + //pointer to the free console messages + freeconsolemessages = consolemessageheap; +} //end of the function InitConsoleMessageHeap +//=========================================================================== +// allocate one console message from the heap +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_consolemessage_t *AllocConsoleMessage(void) +{ + bot_consolemessage_t *message; + message = freeconsolemessages; + if (freeconsolemessages) freeconsolemessages = freeconsolemessages->next; + if (freeconsolemessages) freeconsolemessages->prev = NULL; + return message; +} //end of the function AllocConsoleMessage +//=========================================================================== +// deallocate one console message from the heap +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeConsoleMessage(bot_consolemessage_t *message) +{ + if (freeconsolemessages) freeconsolemessages->prev = message; + message->prev = NULL; + message->next = freeconsolemessages; + freeconsolemessages = message; +} //end of the function FreeConsoleMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotRemoveConsoleMessage(int chatstate, int handle) +{ + bot_consolemessage_t *m, *nextm; + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + + for (m = cs->firstmessage; m; m = nextm) + { + nextm = m->next; + if (m->handle == handle) + { + if (m->next) m->next->prev = m->prev; + else cs->lastmessage = m->prev; + if (m->prev) m->prev->next = m->next; + else cs->firstmessage = m->next; + + FreeConsoleMessage(m); + cs->numconsolemessages--; + break; + } //end if + } //end for +} //end of the function BotRemoveConsoleMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotQueueConsoleMessage(int chatstate, int type, char *message) +{ + bot_consolemessage_t *m; + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + + m = AllocConsoleMessage(); + if (!m) + { + botimport.Print(PRT_ERROR, "empty console message heap\n"); + return; + } //end if + cs->handle++; + if (cs->handle <= 0 || cs->handle > 8192) cs->handle = 1; + m->handle = cs->handle; + m->time = AAS_Time(); + m->type = type; + strncpy(m->message, message, MAX_MESSAGE_SIZE); + m->next = NULL; + if (cs->lastmessage) + { + cs->lastmessage->next = m; + m->prev = cs->lastmessage; + cs->lastmessage = m; + } //end if + else + { + cs->lastmessage = m; + cs->firstmessage = m; + m->prev = NULL; + } //end if + cs->numconsolemessages++; +} //end of the function BotQueueConsoleMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotNextConsoleMessage(int chatstate, bot_consolemessage_t *cm) +{ + bot_chatstate_t *cs; + bot_consolemessage_t *firstmsg; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return 0; + if ((firstmsg = cs->firstmessage)) + { + cm->handle = firstmsg->handle; + cm->time = firstmsg->time; + cm->type = firstmsg->type; + Q_strncpyz(cm->message, firstmsg->message, + sizeof(cm->message)); + + /* We omit setting the two pointers in cm because pointer + * size in the VM differs between the size in the engine on + * 64 bit machines, which would lead to a buffer overflow if + * this functions is called from the VM. The pointers are + * of no interest to functions calling + * BotNextConsoleMessage anyways. + */ + + return cm->handle; + } //end if + return 0; +} //end of the function BotConsoleMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotNumConsoleMessages(int chatstate) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return 0; + return cs->numconsolemessages; +} //end of the function BotNumConsoleMessages +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int IsWhiteSpace(char c) +{ + if ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || c == '(' || c == ')' + || c == '?' || c == ':' + || c == '\''|| c == '/' + || c == ',' || c == '.' + || c == '[' || c == ']' + || c == '-' || c == '_' + || c == '+' || c == '=') return qfalse; + return qtrue; +} //end of the function IsWhiteSpace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotRemoveTildes(char *message) +{ + int i; + + //remove all tildes from the chat message + for (i = 0; message[i]; i++) + { + if (message[i] == '~') + { + memmove(&message[i], &message[i+1], strlen(&message[i+1])+1); + } //end if + } //end for +} //end of the function BotRemoveTildes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void UnifyWhiteSpaces(char *string) +{ + char *ptr, *oldptr; + + for (ptr = oldptr = string; *ptr; oldptr = ptr) + { + while(*ptr && IsWhiteSpace(*ptr)) ptr++; + if (ptr > oldptr) + { + //if not at the start and not at the end of the string + //write only one space + if (oldptr > string && *ptr) *oldptr++ = ' '; + //remove all other white spaces + if (ptr > oldptr) memmove(oldptr, ptr, strlen(ptr)+1); + } //end if + while(*ptr && !IsWhiteSpace(*ptr)) ptr++; + } //end while +} //end of the function UnifyWhiteSpaces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int StringContains(char *str1, char *str2, int casesensitive) +{ + int len, i, j, index; + + if (str1 == NULL || str2 == NULL) return -1; + + len = strlen(str1) - strlen(str2); + index = 0; + for (i = 0; i <= len; i++, str1++, index++) + { + for (j = 0; str2[j]; j++) + { + if (casesensitive) + { + if (str1[j] != str2[j]) break; + } //end if + else + { + if (toupper(str1[j]) != toupper(str2[j])) break; + } //end else + } //end for + if (!str2[j]) return index; + } //end for + return -1; +} //end of the function StringContains +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *StringContainsWord(char *str1, char *str2, int casesensitive) +{ + int len, i, j; + + len = strlen(str1) - strlen(str2); + for (i = 0; i <= len; i++, str1++) + { + //if not at the start of the string + if (i) + { + //skip to the start of the next word + while(*str1 && *str1 != ' ' && *str1 != '.' && *str1 != ',' && *str1 != '!') str1++; + if (!*str1) break; + str1++; + } //end for + //compare the word + for (j = 0; str2[j]; j++) + { + if (casesensitive) + { + if (str1[j] != str2[j]) break; + } //end if + else + { + if (toupper(str1[j]) != toupper(str2[j])) break; + } //end else + } //end for + //if there was a word match + if (!str2[j]) + { + //if the first string has an end of word + if (!str1[j] || str1[j] == ' ' || str1[j] == '.' || str1[j] == ',' || str1[j] == '!') return str1; + } //end if + } //end for + return NULL; +} //end of the function StringContainsWord +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void StringReplaceWords(char *string, char *synonym, char *replacement) +{ + char *str, *str2; + + //find the synonym in the string + str = StringContainsWord(string, synonym, qfalse); + //if the synonym occured in the string + while(str) + { + //if the synonym isn't part of the replacement which is already in the string + //usefull for abreviations + str2 = StringContainsWord(string, replacement, qfalse); + while(str2) + { + if (str2 <= str && str < str2 + strlen(replacement)) break; + str2 = StringContainsWord(str2+1, replacement, qfalse); + } //end while + if (!str2) + { + memmove(str + strlen(replacement), str+strlen(synonym), strlen(str+strlen(synonym))+1); + //append the synonum replacement + Com_Memcpy(str, replacement, strlen(replacement)); + } //end if + //find the next synonym in the string + str = StringContainsWord(str+strlen(replacement), synonym, qfalse); + } //end if +} //end of the function StringReplaceWords +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpSynonymList(bot_synonymlist_t *synlist) +{ + FILE *fp; + bot_synonymlist_t *syn; + bot_synonym_t *synonym; + + fp = Log_FilePointer(); + if (!fp) return; + for (syn = synlist; syn; syn = syn->next) + { + fprintf(fp, "%ld : [", syn->context); + for (synonym = syn->firstsynonym; synonym; synonym = synonym->next) + { + fprintf(fp, "(\"%s\", %1.2f)", synonym->string, synonym->weight); + if (synonym->next) fprintf(fp, ", "); + } //end for + fprintf(fp, "]\n"); + } //end for +} //end of the function BotDumpSynonymList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_synonymlist_t *BotLoadSynonyms(char *filename) +{ + int pass, size, contextlevel, numsynonyms; + unsigned long int context, contextstack[32]; + char *ptr = NULL; + source_t *source; + token_t token; + bot_synonymlist_t *synlist, *lastsyn, *syn; + bot_synonym_t *synonym, *lastsynonym; + + size = 0; + synlist = NULL; //make compiler happy + syn = NULL; //make compiler happy + synonym = NULL; //make compiler happy + //the synonyms are parsed in two phases + for (pass = 0; pass < 2; pass++) + { + // + if (pass && size) ptr = (char *) GetClearedHunkMemory(size); + // + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(filename); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); + return NULL; + } //end if + // + context = 0; + contextlevel = 0; + synlist = NULL; //list synonyms + lastsyn = NULL; //last synonym in the list + // + while(PC_ReadToken(source, &token)) + { + if (token.type == TT_NUMBER) + { + context |= token.intvalue; + contextstack[contextlevel] = token.intvalue; + contextlevel++; + if (contextlevel >= 32) + { + SourceError(source, "more than 32 context levels"); + FreeSource(source); + return NULL; + } //end if + if (!PC_ExpectTokenString(source, "{")) + { + FreeSource(source); + return NULL; + } //end if + } //end if + else if (token.type == TT_PUNCTUATION) + { + if (!strcmp(token.string, "}")) + { + contextlevel--; + if (contextlevel < 0) + { + SourceError(source, "too many }"); + FreeSource(source); + return NULL; + } //end if + context &= ~contextstack[contextlevel]; + } //end if + else if (!strcmp(token.string, "[")) + { + size += sizeof(bot_synonymlist_t); + if (pass) + { + syn = (bot_synonymlist_t *) ptr; + ptr += sizeof(bot_synonymlist_t); + syn->context = context; + syn->firstsynonym = NULL; + syn->next = NULL; + if (lastsyn) lastsyn->next = syn; + else synlist = syn; + lastsyn = syn; + } //end if + numsynonyms = 0; + lastsynonym = NULL; + while(1) + { + size_t len; + if (!PC_ExpectTokenString(source, "(") || + !PC_ExpectTokenType(source, TT_STRING, 0, &token)) + { + FreeSource(source); + return NULL; + } //end if + StripDoubleQuotes(token.string); + if (strlen(token.string) <= 0) + { + SourceError(source, "empty string", token.string); + FreeSource(source); + return NULL; + } //end if + len = strlen(token.string) + 1; + len = PAD(len, sizeof(long)); + size += sizeof(bot_synonym_t) + len; + if (pass) + { + synonym = (bot_synonym_t *) ptr; + ptr += sizeof(bot_synonym_t); + synonym->string = ptr; + ptr += len; + strcpy(synonym->string, token.string); + // + if (lastsynonym) lastsynonym->next = synonym; + else syn->firstsynonym = synonym; + lastsynonym = synonym; + } //end if + numsynonyms++; + if (!PC_ExpectTokenString(source, ",") || + !PC_ExpectTokenType(source, TT_NUMBER, 0, &token) || + !PC_ExpectTokenString(source, ")")) + { + FreeSource(source); + return NULL; + } //end if + if (pass) + { + synonym->weight = token.floatvalue; + syn->totalweight += synonym->weight; + } //end if + if (PC_CheckTokenString(source, "]")) break; + if (!PC_ExpectTokenString(source, ",")) + { + FreeSource(source); + return NULL; + } //end if + } //end while + if (numsynonyms < 2) + { + SourceError(source, "synonym must have at least two entries\n"); + FreeSource(source); + return NULL; + } //end if + } //end else + else + { + SourceError(source, "unexpected %s", token.string); + FreeSource(source); + return NULL; + } //end if + } //end else if + } //end while + // + FreeSource(source); + // + if (contextlevel > 0) + { + SourceError(source, "missing }"); + return NULL; + } //end if + } //end for + botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); + // + //BotDumpSynonymList(synlist); + // + return synlist; +} //end of the function BotLoadSynonyms +//=========================================================================== +// replace all the synonyms in the string +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotReplaceSynonyms(char *string, unsigned long int context) +{ + bot_synonymlist_t *syn; + bot_synonym_t *synonym; + + for (syn = synonyms; syn; syn = syn->next) + { + if (!(syn->context & context)) continue; + for (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next) + { + StringReplaceWords(string, synonym->string, syn->firstsynonym->string); + } //end for + } //end for +} //end of the function BotReplaceSynonyms +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotReplaceWeightedSynonyms(char *string, unsigned long int context) +{ + bot_synonymlist_t *syn; + bot_synonym_t *synonym, *replacement; + float weight, curweight; + + for (syn = synonyms; syn; syn = syn->next) + { + if (!(syn->context & context)) continue; + //choose a weighted random replacement synonym + weight = random() * syn->totalweight; + if (!weight) continue; + curweight = 0; + for (replacement = syn->firstsynonym; replacement; replacement = replacement->next) + { + curweight += replacement->weight; + if (weight < curweight) break; + } //end for + if (!replacement) continue; + //replace all synonyms with the replacement + for (synonym = syn->firstsynonym; synonym; synonym = synonym->next) + { + if (synonym == replacement) continue; + StringReplaceWords(string, synonym->string, replacement->string); + } //end for + } //end for +} //end of the function BotReplaceWeightedSynonyms +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotReplaceReplySynonyms(char *string, unsigned long int context) +{ + char *str1, *str2, *replacement; + bot_synonymlist_t *syn; + bot_synonym_t *synonym; + + for (str1 = string; *str1; ) + { + //go to the start of the next word + while(*str1 && *str1 <= ' ') str1++; + if (!*str1) break; + // + for (syn = synonyms; syn; syn = syn->next) + { + if (!(syn->context & context)) continue; + for (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next) + { + str2 = synonym->string; + //if the synonym is not at the front of the string continue + str2 = StringContainsWord(str1, synonym->string, qfalse); + if (!str2 || str2 != str1) continue; + // + replacement = syn->firstsynonym->string; + //if the replacement IS in front of the string continue + str2 = StringContainsWord(str1, replacement, qfalse); + if (str2 && str2 == str1) continue; + // + memmove(str1 + strlen(replacement), str1+strlen(synonym->string), + strlen(str1+strlen(synonym->string)) + 1); + //append the synonum replacement + Com_Memcpy(str1, replacement, strlen(replacement)); + // + break; + } //end for + //if a synonym has been replaced + if (synonym) break; + } //end for + //skip over this word + while(*str1 && *str1 > ' ') str1++; + if (!*str1) break; + } //end while +} //end of the function BotReplaceReplySynonyms +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadChatMessage(source_t *source, char *chatmessagestring) +{ + char *ptr; + token_t token; + + ptr = chatmessagestring; + *ptr = 0; + // + while(1) + { + if (!PC_ExpectAnyToken(source, &token)) return qfalse; + //fixed string + if (token.type == TT_STRING) + { + StripDoubleQuotes(token.string); + if (strlen(ptr) + strlen(token.string) + 1 > MAX_MESSAGE_SIZE) + { + SourceError(source, "chat message too long\n"); + return qfalse; + } //end if + strcat(ptr, token.string); + } //end else if + //variable string + else if (token.type == TT_NUMBER && (token.subtype & TT_INTEGER)) + { + if (strlen(ptr) + 7 > MAX_MESSAGE_SIZE) + { + SourceError(source, "chat message too long\n"); + return qfalse; + } //end if + sprintf(&ptr[strlen(ptr)], "%cv%ld%c", ESCAPE_CHAR, token.intvalue, ESCAPE_CHAR); + } //end if + //random string + else if (token.type == TT_NAME) + { + if (strlen(ptr) + 7 > MAX_MESSAGE_SIZE) + { + SourceError(source, "chat message too long\n"); + return qfalse; + } //end if + sprintf(&ptr[strlen(ptr)], "%cr%s%c", ESCAPE_CHAR, token.string, ESCAPE_CHAR); + } //end else if + else + { + SourceError(source, "unknown message component %s\n", token.string); + return qfalse; + } //end else + if (PC_CheckTokenString(source, ";")) break; + if (!PC_ExpectTokenString(source, ",")) return qfalse; + } //end while + // + return qtrue; +} //end of the function BotLoadChatMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpRandomStringList(bot_randomlist_t *randomlist) +{ + FILE *fp; + bot_randomlist_t *random; + bot_randomstring_t *rs; + + fp = Log_FilePointer(); + if (!fp) return; + for (random = randomlist; random; random = random->next) + { + fprintf(fp, "%s = {", random->string); + for (rs = random->firstrandomstring; rs; rs = rs->next) + { + fprintf(fp, "\"%s\"", rs->string); + if (rs->next) fprintf(fp, ", "); + else fprintf(fp, "}\n"); + } //end for + } //end for +} //end of the function BotDumpRandomStringList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_randomlist_t *BotLoadRandomStrings(char *filename) +{ + int pass, size; + char *ptr = NULL, chatmessagestring[MAX_MESSAGE_SIZE]; + source_t *source; + token_t token; + bot_randomlist_t *randomlist, *lastrandom, *random; + bot_randomstring_t *randomstring; + +#ifdef DEBUG + int starttime = Sys_MilliSeconds(); +#endif //DEBUG + + size = 0; + randomlist = NULL; + random = NULL; + //the synonyms are parsed in two phases + for (pass = 0; pass < 2; pass++) + { + // + if (pass && size) ptr = (char *) GetClearedHunkMemory(size); + // + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(filename); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); + return NULL; + } //end if + // + randomlist = NULL; //list + lastrandom = NULL; //last + // + while(PC_ReadToken(source, &token)) + { + size_t len; + if (token.type != TT_NAME) + { + SourceError(source, "unknown random %s", token.string); + FreeSource(source); + return NULL; + } //end if + len = strlen(token.string) + 1; + len = PAD(len, sizeof(long)); + size += sizeof(bot_randomlist_t) + len; + if (pass) + { + random = (bot_randomlist_t *) ptr; + ptr += sizeof(bot_randomlist_t); + random->string = ptr; + ptr += len; + strcpy(random->string, token.string); + random->firstrandomstring = NULL; + random->numstrings = 0; + // + if (lastrandom) lastrandom->next = random; + else randomlist = random; + lastrandom = random; + } //end if + if (!PC_ExpectTokenString(source, "=") || + !PC_ExpectTokenString(source, "{")) + { + FreeSource(source); + return NULL; + } //end if + while(!PC_CheckTokenString(source, "}")) + { + size_t len; + if (!BotLoadChatMessage(source, chatmessagestring)) + { + FreeSource(source); + return NULL; + } //end if + len = strlen(chatmessagestring) + 1; + len = PAD(len, sizeof(long)); + size += sizeof(bot_randomstring_t) + len; + if (pass) + { + randomstring = (bot_randomstring_t *) ptr; + ptr += sizeof(bot_randomstring_t); + randomstring->string = ptr; + ptr += len; + strcpy(randomstring->string, chatmessagestring); + // + random->numstrings++; + randomstring->next = random->firstrandomstring; + random->firstrandomstring = randomstring; + } //end if + } //end while + } //end while + //free the source after one pass + FreeSource(source); + } //end for + botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); + // +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "random strings %d msec\n", Sys_MilliSeconds() - starttime); + //BotDumpRandomStringList(randomlist); +#endif //DEBUG + // + return randomlist; +} //end of the function BotLoadRandomStrings +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *RandomString(char *name) +{ + bot_randomlist_t *random; + bot_randomstring_t *rs; + int i; + + for (random = randomstrings; random; random = random->next) + { + if (!strcmp(random->string, name)) + { + i = random() * random->numstrings; + for (rs = random->firstrandomstring; rs; rs = rs->next) + { + if (--i < 0) break; + } //end for + if (rs) + { + return rs->string; + } //end if + } //end for + } //end for + return NULL; +} //end of the function RandomString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpMatchTemplates(bot_matchtemplate_t *matches) +{ + FILE *fp; + bot_matchtemplate_t *mt; + bot_matchpiece_t *mp; + bot_matchstring_t *ms; + + fp = Log_FilePointer(); + if (!fp) return; + for (mt = matches; mt; mt = mt->next) + { + fprintf(fp, "{ " ); + for (mp = mt->first; mp; mp = mp->next) + { + if (mp->type == MT_STRING) + { + for (ms = mp->firststring; ms; ms = ms->next) + { + fprintf(fp, "\"%s\"", ms->string); + if (ms->next) fprintf(fp, "|"); + } //end for + } //end if + else if (mp->type == MT_VARIABLE) + { + fprintf(fp, "%d", mp->variable); + } //end else if + if (mp->next) fprintf(fp, ", "); + } //end for + fprintf(fp, " = (%d, %d);}\n", mt->type, mt->subtype); + } //end for +} //end of the function BotDumpMatchTemplates +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFreeMatchPieces(bot_matchpiece_t *matchpieces) +{ + bot_matchpiece_t *mp, *nextmp; + bot_matchstring_t *ms, *nextms; + + for (mp = matchpieces; mp; mp = nextmp) + { + nextmp = mp->next; + if (mp->type == MT_STRING) + { + for (ms = mp->firststring; ms; ms = nextms) + { + nextms = ms->next; + FreeMemory(ms); + } //end for + } //end if + FreeMemory(mp); + } //end for +} //end of the function BotFreeMatchPieces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_matchpiece_t *BotLoadMatchPieces(source_t *source, char *endtoken) +{ + int lastwasvariable, emptystring; + token_t token; + bot_matchpiece_t *matchpiece, *firstpiece, *lastpiece; + bot_matchstring_t *matchstring, *lastmatchstring; + + firstpiece = NULL; + lastpiece = NULL; + // + lastwasvariable = qfalse; + // + while(PC_ReadToken(source, &token)) + { + if (token.type == TT_NUMBER && (token.subtype & TT_INTEGER)) + { + if (token.intvalue < 0 || token.intvalue >= MAX_MATCHVARIABLES) + { + SourceError(source, "can't have more than %d match variables\n", MAX_MATCHVARIABLES); + FreeSource(source); + BotFreeMatchPieces(firstpiece); + return NULL; + } //end if + if (lastwasvariable) + { + SourceError(source, "not allowed to have adjacent variables\n"); + FreeSource(source); + BotFreeMatchPieces(firstpiece); + return NULL; + } //end if + lastwasvariable = qtrue; + // + matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t)); + matchpiece->type = MT_VARIABLE; + matchpiece->variable = token.intvalue; + matchpiece->next = NULL; + if (lastpiece) lastpiece->next = matchpiece; + else firstpiece = matchpiece; + lastpiece = matchpiece; + } //end if + else if (token.type == TT_STRING) + { + // + matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t)); + matchpiece->firststring = NULL; + matchpiece->type = MT_STRING; + matchpiece->variable = 0; + matchpiece->next = NULL; + if (lastpiece) lastpiece->next = matchpiece; + else firstpiece = matchpiece; + lastpiece = matchpiece; + // + lastmatchstring = NULL; + emptystring = qfalse; + // + do + { + if (matchpiece->firststring) + { + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) + { + FreeSource(source); + BotFreeMatchPieces(firstpiece); + return NULL; + } //end if + } //end if + StripDoubleQuotes(token.string); + matchstring = (bot_matchstring_t *) GetClearedHunkMemory(sizeof(bot_matchstring_t) + strlen(token.string) + 1); + matchstring->string = (char *) matchstring + sizeof(bot_matchstring_t); + strcpy(matchstring->string, token.string); + if (!strlen(token.string)) emptystring = qtrue; + matchstring->next = NULL; + if (lastmatchstring) lastmatchstring->next = matchstring; + else matchpiece->firststring = matchstring; + lastmatchstring = matchstring; + } while(PC_CheckTokenString(source, "|")); + //if there was no empty string found + if (!emptystring) lastwasvariable = qfalse; + } //end if + else + { + SourceError(source, "invalid token %s\n", token.string); + FreeSource(source); + BotFreeMatchPieces(firstpiece); + return NULL; + } //end else + if (PC_CheckTokenString(source, endtoken)) break; + if (!PC_ExpectTokenString(source, ",")) + { + FreeSource(source); + BotFreeMatchPieces(firstpiece); + return NULL; + } //end if + } //end while + return firstpiece; +} //end of the function BotLoadMatchPieces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFreeMatchTemplates(bot_matchtemplate_t *mt) +{ + bot_matchtemplate_t *nextmt; + + for (; mt; mt = nextmt) + { + nextmt = mt->next; + BotFreeMatchPieces(mt->first); + FreeMemory(mt); + } //end for +} //end of the function BotFreeMatchTemplates +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_matchtemplate_t *BotLoadMatchTemplates(char *matchfile) +{ + source_t *source; + token_t token; + bot_matchtemplate_t *matchtemplate, *matches, *lastmatch; + unsigned long int context; + + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(matchfile); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", matchfile); + return NULL; + } //end if + // + matches = NULL; //list with matches + lastmatch = NULL; //last match in the list + + while(PC_ReadToken(source, &token)) + { + if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER)) + { + SourceError(source, "expected integer, found %s\n", token.string); + BotFreeMatchTemplates(matches); + FreeSource(source); + return NULL; + } //end if + //the context + context = token.intvalue; + // + if (!PC_ExpectTokenString(source, "{")) + { + BotFreeMatchTemplates(matches); + FreeSource(source); + return NULL; + } //end if + // + while(PC_ReadToken(source, &token)) + { + if (!strcmp(token.string, "}")) break; + // + PC_UnreadLastToken(source); + // + matchtemplate = (bot_matchtemplate_t *) GetClearedHunkMemory(sizeof(bot_matchtemplate_t)); + matchtemplate->context = context; + matchtemplate->next = NULL; + //add the match template to the list + if (lastmatch) lastmatch->next = matchtemplate; + else matches = matchtemplate; + lastmatch = matchtemplate; + //load the match template + matchtemplate->first = BotLoadMatchPieces(source, "="); + if (!matchtemplate->first) + { + BotFreeMatchTemplates(matches); + return NULL; + } //end if + //read the match type + if (!PC_ExpectTokenString(source, "(") || + !PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) + { + BotFreeMatchTemplates(matches); + FreeSource(source); + return NULL; + } //end if + matchtemplate->type = token.intvalue; + //read the match subtype + if (!PC_ExpectTokenString(source, ",") || + !PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) + { + BotFreeMatchTemplates(matches); + FreeSource(source); + return NULL; + } //end if + matchtemplate->subtype = token.intvalue; + //read trailing punctuations + if (!PC_ExpectTokenString(source, ")") || + !PC_ExpectTokenString(source, ";")) + { + BotFreeMatchTemplates(matches); + FreeSource(source); + return NULL; + } //end if + } //end while + } //end while + //free the source + FreeSource(source); + botimport.Print(PRT_MESSAGE, "loaded %s\n", matchfile); + // + //BotDumpMatchTemplates(matches); + // + return matches; +} //end of the function BotLoadMatchTemplates +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int StringsMatch(bot_matchpiece_t *pieces, bot_match_t *match) +{ + int lastvariable, index; + char *strptr, *newstrptr; + bot_matchpiece_t *mp; + bot_matchstring_t *ms; + + //no last variable + lastvariable = -1; + //pointer to the string to compare the match string with + strptr = match->string; + //Log_Write("match: %s", strptr); + //compare the string with the current match string + for (mp = pieces; mp; mp = mp->next) + { + //if it is a piece of string + if (mp->type == MT_STRING) + { + newstrptr = NULL; + for (ms = mp->firststring; ms; ms = ms->next) + { + if (!strlen(ms->string)) + { + newstrptr = strptr; + break; + } //end if + //Log_Write("MT_STRING: %s", mp->string); + index = StringContains(strptr, ms->string, qfalse); + if (index >= 0) + { + newstrptr = strptr + index; + if (lastvariable >= 0) + { + match->variables[lastvariable].length = + (newstrptr - match->string) - match->variables[lastvariable].offset; + //newstrptr - match->variables[lastvariable].ptr; + lastvariable = -1; + break; + } //end if + else if (index == 0) + { + break; + } //end else + newstrptr = NULL; + } //end if + } //end for + if (!newstrptr) return qfalse; + strptr = newstrptr + strlen(ms->string); + } //end if + //if it is a variable piece of string + else if (mp->type == MT_VARIABLE) + { + //Log_Write("MT_VARIABLE"); + match->variables[mp->variable].offset = strptr - match->string; + lastvariable = mp->variable; + } //end else if + } //end for + //if a match was found + if (!mp && (lastvariable >= 0 || !strlen(strptr))) + { + //if the last piece was a variable string + if (lastvariable >= 0) + { + assert( match->variables[lastvariable].offset >= 0 ); + match->variables[lastvariable].length = + strlen(&match->string[ (int) match->variables[lastvariable].offset]); + } //end if + return qtrue; + } //end if + return qfalse; +} //end of the function StringsMatch +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotFindMatch(char *str, bot_match_t *match, unsigned long int context) +{ + int i; + bot_matchtemplate_t *ms; + + strncpy(match->string, str, MAX_MESSAGE_SIZE); + //remove any trailing enters + while(strlen(match->string) && + match->string[strlen(match->string)-1] == '\n') + { + match->string[strlen(match->string)-1] = '\0'; + } //end while + //compare the string with all the match strings + for (ms = matchtemplates; ms; ms = ms->next) + { + if (!(ms->context & context)) continue; + //reset the match variable offsets + for (i = 0; i < MAX_MATCHVARIABLES; i++) match->variables[i].offset = -1; + // + if (StringsMatch(ms->first, match)) + { + match->type = ms->type; + match->subtype = ms->subtype; + return qtrue; + } //end if + } //end for + return qfalse; +} //end of the function BotFindMatch +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotMatchVariable(bot_match_t *match, int variable, char *buf, int size) +{ + if (variable < 0 || variable >= MAX_MATCHVARIABLES) + { + botimport.Print(PRT_FATAL, "BotMatchVariable: variable out of range\n"); + strcpy(buf, ""); + return; + } //end if + + if (match->variables[variable].offset >= 0) + { + if (match->variables[variable].length < size) + size = match->variables[variable].length+1; + assert( match->variables[variable].offset >= 0 ); + strncpy(buf, &match->string[ (int) match->variables[variable].offset], size-1); + buf[size-1] = '\0'; + } //end if + else + { + strcpy(buf, ""); + } //end else + return; +} //end of the function BotMatchVariable +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_stringlist_t *BotFindStringInList(bot_stringlist_t *list, char *string) +{ + bot_stringlist_t *s; + + for (s = list; s; s = s->next) + { + if (!strcmp(s->string, string)) return s; + } //end for + return NULL; +} //end of the function BotFindStringInList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_stringlist_t *BotCheckChatMessageIntegrety(char *message, bot_stringlist_t *stringlist) +{ + int i; + char *msgptr; + char temp[MAX_MESSAGE_SIZE]; + bot_stringlist_t *s; + + msgptr = message; + // + while(*msgptr) + { + if (*msgptr == ESCAPE_CHAR) + { + msgptr++; + switch(*msgptr) + { + case 'v': //variable + { + //step over the 'v' + msgptr++; + while(*msgptr && *msgptr != ESCAPE_CHAR) msgptr++; + //step over the trailing escape char + if (*msgptr) msgptr++; + break; + } //end case + case 'r': //random + { + //step over the 'r' + msgptr++; + for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++) + { + temp[i] = *msgptr++; + } //end while + temp[i] = '\0'; + //step over the trailing escape char + if (*msgptr) msgptr++; + //find the random keyword + if (!RandomString(temp)) + { + if (!BotFindStringInList(stringlist, temp)) + { + Log_Write("%s = {\"%s\"} //MISSING RANDOM\r\n", temp, temp); + s = GetClearedMemory(sizeof(bot_stringlist_t) + strlen(temp) + 1); + s->string = (char *) s + sizeof(bot_stringlist_t); + strcpy(s->string, temp); + s->next = stringlist; + stringlist = s; + } //end if + } //end if + break; + } //end case + default: + { + botimport.Print(PRT_FATAL, "BotCheckChatMessageIntegrety: message \"%s\" invalid escape char\n", message); + break; + } //end default + } //end switch + } //end if + else + { + msgptr++; + } //end else + } //end while + return stringlist; +} //end of the function BotCheckChatMessageIntegrety +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotCheckInitialChatIntegrety(bot_chat_t *chat) +{ + bot_chattype_t *t; + bot_chatmessage_t *cm; + bot_stringlist_t *stringlist, *s, *nexts; + + stringlist = NULL; + for (t = chat->types; t; t = t->next) + { + for (cm = t->firstchatmessage; cm; cm = cm->next) + { + stringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist); + } //end for + } //end for + for (s = stringlist; s; s = nexts) + { + nexts = s->next; + FreeMemory(s); + } //end for +} //end of the function BotCheckInitialChatIntegrety +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotCheckReplyChatIntegrety(bot_replychat_t *replychat) +{ + bot_replychat_t *rp; + bot_chatmessage_t *cm; + bot_stringlist_t *stringlist, *s, *nexts; + + stringlist = NULL; + for (rp = replychat; rp; rp = rp->next) + { + for (cm = rp->firstchatmessage; cm; cm = cm->next) + { + stringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist); + } //end for + } //end for + for (s = stringlist; s; s = nexts) + { + nexts = s->next; + FreeMemory(s); + } //end for +} //end of the function BotCheckReplyChatIntegrety +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpReplyChat(bot_replychat_t *replychat) +{ + FILE *fp; + bot_replychat_t *rp; + bot_replychatkey_t *key; + bot_chatmessage_t *cm; + bot_matchpiece_t *mp; + + fp = Log_FilePointer(); + if (!fp) return; + fprintf(fp, "BotDumpReplyChat:\n"); + for (rp = replychat; rp; rp = rp->next) + { + fprintf(fp, "["); + for (key = rp->keys; key; key = key->next) + { + if (key->flags & RCKFL_AND) fprintf(fp, "&"); + else if (key->flags & RCKFL_NOT) fprintf(fp, "!"); + // + if (key->flags & RCKFL_NAME) fprintf(fp, "name"); + else if (key->flags & RCKFL_GENDERFEMALE) fprintf(fp, "female"); + else if (key->flags & RCKFL_GENDERMALE) fprintf(fp, "male"); + else if (key->flags & RCKFL_GENDERLESS) fprintf(fp, "it"); + else if (key->flags & RCKFL_VARIABLES) + { + fprintf(fp, "("); + for (mp = key->match; mp; mp = mp->next) + { + if (mp->type == MT_STRING) fprintf(fp, "\"%s\"", mp->firststring->string); + else fprintf(fp, "%d", mp->variable); + if (mp->next) fprintf(fp, ", "); + } //end for + fprintf(fp, ")"); + } //end if + else if (key->flags & RCKFL_STRING) + { + fprintf(fp, "\"%s\"", key->string); + } //end if + if (key->next) fprintf(fp, ", "); + else fprintf(fp, "] = %1.0f\n", rp->priority); + } //end for + fprintf(fp, "{\n"); + for (cm = rp->firstchatmessage; cm; cm = cm->next) + { + fprintf(fp, "\t\"%s\";\n", cm->chatmessage); + } //end for + fprintf(fp, "}\n"); + } //end for +} //end of the function BotDumpReplyChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFreeReplyChat(bot_replychat_t *replychat) +{ + bot_replychat_t *rp, *nextrp; + bot_replychatkey_t *key, *nextkey; + bot_chatmessage_t *cm, *nextcm; + + for (rp = replychat; rp; rp = nextrp) + { + nextrp = rp->next; + for (key = rp->keys; key; key = nextkey) + { + nextkey = key->next; + if (key->match) BotFreeMatchPieces(key->match); + if (key->string) FreeMemory(key->string); + FreeMemory(key); + } //end for + for (cm = rp->firstchatmessage; cm; cm = nextcm) + { + nextcm = cm->next; + FreeMemory(cm); + } //end for + FreeMemory(rp); + } //end for +} //end of the function BotFreeReplyChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotCheckValidReplyChatKeySet(source_t *source, bot_replychatkey_t *keys) +{ + int allprefixed, hasvariableskey, hasstringkey; + bot_matchpiece_t *m; + bot_matchstring_t *ms; + bot_replychatkey_t *key, *key2; + + // + allprefixed = qtrue; + hasvariableskey = hasstringkey = qfalse; + for (key = keys; key; key = key->next) + { + if (!(key->flags & (RCKFL_AND|RCKFL_NOT))) + { + allprefixed = qfalse; + if (key->flags & RCKFL_VARIABLES) + { + for (m = key->match; m; m = m->next) + { + if (m->type == MT_VARIABLE) hasvariableskey = qtrue; + } //end for + } //end if + else if (key->flags & RCKFL_STRING) + { + hasstringkey = qtrue; + } //end else if + } //end if + else if ((key->flags & RCKFL_AND) && (key->flags & RCKFL_STRING)) + { + for (key2 = keys; key2; key2 = key2->next) + { + if (key2 == key) continue; + if (key2->flags & RCKFL_NOT) continue; + if (key2->flags & RCKFL_VARIABLES) + { + for (m = key2->match; m; m = m->next) + { + if (m->type == MT_STRING) + { + for (ms = m->firststring; ms; ms = ms->next) + { + if (StringContains(ms->string, key->string, qfalse) != -1) + { + break; + } //end if + } //end for + if (ms) break; + } //end if + else if (m->type == MT_VARIABLE) + { + break; + } //end if + } //end for + if (!m) + { + SourceWarning(source, "one of the match templates does not " + "leave space for the key %s with the & prefix", key->string); + } //end if + } //end if + } //end for + } //end else + if ((key->flags & RCKFL_NOT) && (key->flags & RCKFL_STRING)) + { + for (key2 = keys; key2; key2 = key2->next) + { + if (key2 == key) continue; + if (key2->flags & RCKFL_NOT) continue; + if (key2->flags & RCKFL_STRING) + { + if (StringContains(key2->string, key->string, qfalse) != -1) + { + SourceWarning(source, "the key %s with prefix ! is inside the key %s", key->string, key2->string); + } //end if + } //end if + else if (key2->flags & RCKFL_VARIABLES) + { + for (m = key2->match; m; m = m->next) + { + if (m->type == MT_STRING) + { + for (ms = m->firststring; ms; ms = ms->next) + { + if (StringContains(ms->string, key->string, qfalse) != -1) + { + SourceWarning(source, "the key %s with prefix ! is inside " + "the match template string %s", key->string, ms->string); + } //end if + } //end for + } //end if + } //end for + } //end else if + } //end for + } //end if + } //end for + if (allprefixed) SourceWarning(source, "all keys have a & or ! prefix"); + if (hasvariableskey && hasstringkey) + { + SourceWarning(source, "variables from the match template(s) could be " + "invalid when outputting one of the chat messages"); + } //end if +} //end of the function BotCheckValidReplyChatKeySet +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_replychat_t *BotLoadReplyChat(char *filename) +{ + char chatmessagestring[MAX_MESSAGE_SIZE]; + char namebuffer[MAX_MESSAGE_SIZE]; + source_t *source; + token_t token; + bot_chatmessage_t *chatmessage = NULL; + bot_replychat_t *replychat, *replychatlist; + bot_replychatkey_t *key; + + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(filename); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); + return NULL; + } //end if + // + replychatlist = NULL; + // + while(PC_ReadToken(source, &token)) + { + if (strcmp(token.string, "[")) + { + SourceError(source, "expected [, found %s", token.string); + BotFreeReplyChat(replychatlist); + FreeSource(source); + return NULL; + } //end if + // + replychat = GetClearedHunkMemory(sizeof(bot_replychat_t)); + replychat->keys = NULL; + replychat->next = replychatlist; + replychatlist = replychat; + //read the keys, there must be at least one key + do + { + //allocate a key + key = (bot_replychatkey_t *) GetClearedHunkMemory(sizeof(bot_replychatkey_t)); + key->flags = 0; + key->string = NULL; + key->match = NULL; + key->next = replychat->keys; + replychat->keys = key; + //check for MUST BE PRESENT and MUST BE ABSENT keys + if (PC_CheckTokenString(source, "&")) key->flags |= RCKFL_AND; + else if (PC_CheckTokenString(source, "!")) key->flags |= RCKFL_NOT; + //special keys + if (PC_CheckTokenString(source, "name")) key->flags |= RCKFL_NAME; + else if (PC_CheckTokenString(source, "female")) key->flags |= RCKFL_GENDERFEMALE; + else if (PC_CheckTokenString(source, "male")) key->flags |= RCKFL_GENDERMALE; + else if (PC_CheckTokenString(source, "it")) key->flags |= RCKFL_GENDERLESS; + else if (PC_CheckTokenString(source, "(")) //match key + { + key->flags |= RCKFL_VARIABLES; + key->match = BotLoadMatchPieces(source, ")"); + if (!key->match) + { + BotFreeReplyChat(replychatlist); + return NULL; + } //end if + } //end else if + else if (PC_CheckTokenString(source, "<")) //bot names + { + key->flags |= RCKFL_BOTNAMES; + strcpy(namebuffer, ""); + do + { + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) + { + BotFreeReplyChat(replychatlist); + FreeSource(source); + return NULL; + } //end if + StripDoubleQuotes(token.string); + if (strlen(namebuffer)) strcat(namebuffer, "\\"); + strcat(namebuffer, token.string); + } while(PC_CheckTokenString(source, ",")); + if (!PC_ExpectTokenString(source, ">")) + { + BotFreeReplyChat(replychatlist); + FreeSource(source); + return NULL; + } //end if + key->string = (char *) GetClearedHunkMemory(strlen(namebuffer) + 1); + strcpy(key->string, namebuffer); + } //end else if + else //normal string key + { + key->flags |= RCKFL_STRING; + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) + { + BotFreeReplyChat(replychatlist); + FreeSource(source); + return NULL; + } //end if + StripDoubleQuotes(token.string); + key->string = (char *) GetClearedHunkMemory(strlen(token.string) + 1); + strcpy(key->string, token.string); + } //end else + // + PC_CheckTokenString(source, ","); + } while(!PC_CheckTokenString(source, "]")); + // + BotCheckValidReplyChatKeySet(source, replychat->keys); + //read the = sign and the priority + if (!PC_ExpectTokenString(source, "=") || + !PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) + { + BotFreeReplyChat(replychatlist); + FreeSource(source); + return NULL; + } //end if + replychat->priority = token.floatvalue; + //read the leading { + if (!PC_ExpectTokenString(source, "{")) + { + BotFreeReplyChat(replychatlist); + FreeSource(source); + return NULL; + } //end if + replychat->numchatmessages = 0; + //while the trailing } is not found + while(!PC_CheckTokenString(source, "}")) + { + if (!BotLoadChatMessage(source, chatmessagestring)) + { + BotFreeReplyChat(replychatlist); + FreeSource(source); + return NULL; + } //end if + chatmessage = (bot_chatmessage_t *) GetClearedHunkMemory(sizeof(bot_chatmessage_t) + strlen(chatmessagestring) + 1); + chatmessage->chatmessage = (char *) chatmessage + sizeof(bot_chatmessage_t); + strcpy(chatmessage->chatmessage, chatmessagestring); + chatmessage->time = -2*CHATMESSAGE_RECENTTIME; + chatmessage->next = replychat->firstchatmessage; + //add the chat message to the reply chat + replychat->firstchatmessage = chatmessage; + replychat->numchatmessages++; + } //end while + } //end while + FreeSource(source); + botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); + // + //BotDumpReplyChat(replychatlist); + if (bot_developer) + { + BotCheckReplyChatIntegrety(replychatlist); + } //end if + // + if (!replychatlist) botimport.Print(PRT_MESSAGE, "no rchats\n"); + // + return replychatlist; +} //end of the function BotLoadReplyChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpInitialChat(bot_chat_t *chat) +{ + bot_chattype_t *t; + bot_chatmessage_t *m; + + Log_Write("{"); + for (t = chat->types; t; t = t->next) + { + Log_Write(" type \"%s\"", t->name); + Log_Write(" {"); + Log_Write(" numchatmessages = %d", t->numchatmessages); + for (m = t->firstchatmessage; m; m = m->next) + { + Log_Write(" \"%s\"", m->chatmessage); + } //end for + Log_Write(" }"); + } //end for + Log_Write("}"); +} //end of the function BotDumpInitialChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_chat_t *BotLoadInitialChat(char *chatfile, char *chatname) +{ + int pass, foundchat, indent, size; + char *ptr = NULL; + char chatmessagestring[MAX_MESSAGE_SIZE]; + source_t *source; + token_t token; + bot_chat_t *chat = NULL; + bot_chattype_t *chattype = NULL; + bot_chatmessage_t *chatmessage = NULL; +#ifdef DEBUG + int starttime; + + starttime = Sys_MilliSeconds(); +#endif //DEBUG + // + size = 0; + foundchat = qfalse; + //a bot chat is parsed in two phases + for (pass = 0; pass < 2; pass++) + { + //allocate memory + if (pass && size) ptr = (char *) GetClearedMemory(size); + //load the source file + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(chatfile); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", chatfile); + return NULL; + } //end if + //chat structure + if (pass) + { + chat = (bot_chat_t *) ptr; + ptr += sizeof(bot_chat_t); + } //end if + size = sizeof(bot_chat_t); + // + while(PC_ReadToken(source, &token)) + { + if (!strcmp(token.string, "chat")) + { + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) + { + FreeSource(source); + return NULL; + } //end if + StripDoubleQuotes(token.string); + //after the chat name we expect a opening brace + if (!PC_ExpectTokenString(source, "{")) + { + FreeSource(source); + return NULL; + } //end if + //if the chat name is found + if (!Q_stricmp(token.string, chatname)) + { + foundchat = qtrue; + //read the chat types + while(1) + { + if (!PC_ExpectAnyToken(source, &token)) + { + FreeSource(source); + return NULL; + } //end if + if (!strcmp(token.string, "}")) break; + if (strcmp(token.string, "type")) + { + SourceError(source, "expected type found %s\n", token.string); + FreeSource(source); + return NULL; + } //end if + //expect the chat type name + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token) || + !PC_ExpectTokenString(source, "{")) + { + FreeSource(source); + return NULL; + } //end if + StripDoubleQuotes(token.string); + if (pass) + { + chattype = (bot_chattype_t *) ptr; + strncpy(chattype->name, token.string, MAX_CHATTYPE_NAME); + chattype->firstchatmessage = NULL; + //add the chat type to the chat + chattype->next = chat->types; + chat->types = chattype; + // + ptr += sizeof(bot_chattype_t); + } //end if + size += sizeof(bot_chattype_t); + //read the chat messages + while(!PC_CheckTokenString(source, "}")) + { + size_t len; + if (!BotLoadChatMessage(source, chatmessagestring)) + { + FreeSource(source); + return NULL; + } //end if + len = strlen(chatmessagestring) + 1; + len = PAD(len, sizeof(long)); + if (pass) + { + chatmessage = (bot_chatmessage_t *) ptr; + chatmessage->time = -2*CHATMESSAGE_RECENTTIME; + //put the chat message in the list + chatmessage->next = chattype->firstchatmessage; + chattype->firstchatmessage = chatmessage; + //store the chat message + ptr += sizeof(bot_chatmessage_t); + chatmessage->chatmessage = ptr; + strcpy(chatmessage->chatmessage, chatmessagestring); + ptr += len; + //the number of chat messages increased + chattype->numchatmessages++; + } //end if + size += sizeof(bot_chatmessage_t) + len; + } //end if + } //end while + } //end if + else //skip the bot chat + { + indent = 1; + while(indent) + { + if (!PC_ExpectAnyToken(source, &token)) + { + FreeSource(source); + return NULL; + } //end if + if (!strcmp(token.string, "{")) indent++; + else if (!strcmp(token.string, "}")) indent--; + } //end while + } //end else + } //end if + else + { + SourceError(source, "unknown definition %s\n", token.string); + FreeSource(source); + return NULL; + } //end else + } //end while + //free the source + FreeSource(source); + //if the requested character is not found + if (!foundchat) + { + botimport.Print(PRT_ERROR, "couldn't find chat %s in %s\n", chatname, chatfile); + return NULL; + } //end if + } //end for + // + botimport.Print(PRT_MESSAGE, "loaded %s from %s\n", chatname, chatfile); + // + //BotDumpInitialChat(chat); + if (bot_developer) + { + BotCheckInitialChatIntegrety(chat); + } //end if +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "initial chats loaded in %d msec\n", Sys_MilliSeconds() - starttime); +#endif //DEBUG + //character was read succesfully + return chat; +} //end of the function BotLoadInitialChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFreeChatFile(int chatstate) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + if (cs->chat) FreeMemory(cs->chat); + cs->chat = NULL; +} //end of the function BotFreeChatFile +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadChatFile(int chatstate, char *chatfile, char *chatname) +{ + bot_chatstate_t *cs; + int n, avail = 0; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return BLERR_CANNOTLOADICHAT; + BotFreeChatFile(chatstate); + + if (!LibVarGetValue("bot_reloadcharacters")) + { + avail = -1; + for( n = 0; n < MAX_CLIENTS; n++ ) { + if( !ichatdata[n] ) { + if( avail == -1 ) { + avail = n; + } + continue; + } + if( strcmp( chatfile, ichatdata[n]->filename ) != 0 ) { + continue; + } + if( strcmp( chatname, ichatdata[n]->chatname ) != 0 ) { + continue; + } + cs->chat = ichatdata[n]->chat; + // botimport.Print( PRT_MESSAGE, "retained %s from %s\n", chatname, chatfile ); + return BLERR_NOERROR; + } + + if( avail == -1 ) { + botimport.Print(PRT_FATAL, "ichatdata table full; couldn't load chat %s from %s\n", chatname, chatfile); + return BLERR_CANNOTLOADICHAT; + } + } + + cs->chat = BotLoadInitialChat(chatfile, chatname); + if (!cs->chat) + { + botimport.Print(PRT_FATAL, "couldn't load chat %s from %s\n", chatname, chatfile); + return BLERR_CANNOTLOADICHAT; + } //end if + if (!LibVarGetValue("bot_reloadcharacters")) + { + ichatdata[avail] = GetClearedMemory( sizeof(bot_ichatdata_t) ); + ichatdata[avail]->chat = cs->chat; + Q_strncpyz( ichatdata[avail]->chatname, chatname, sizeof(ichatdata[avail]->chatname) ); + Q_strncpyz( ichatdata[avail]->filename, chatfile, sizeof(ichatdata[avail]->filename) ); + } //end if + + return BLERR_NOERROR; +} //end of the function BotLoadChatFile +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotExpandChatMessage(char *outmessage, char *message, unsigned long mcontext, + bot_match_t *match, unsigned long vcontext, int reply) +{ + int num, len, i, expansion; + char *outputbuf, *ptr, *msgptr; + char temp[MAX_MESSAGE_SIZE]; + + expansion = qfalse; + msgptr = message; + outputbuf = outmessage; + len = 0; + // + while(*msgptr) + { + if (*msgptr == ESCAPE_CHAR) + { + msgptr++; + switch(*msgptr) + { + case 'v': //variable + { + msgptr++; + num = 0; + while(*msgptr && *msgptr != ESCAPE_CHAR) + { + num = num * 10 + (*msgptr++) - '0'; + } //end while + //step over the trailing escape char + if (*msgptr) msgptr++; + if (num > MAX_MATCHVARIABLES) + { + botimport.Print(PRT_ERROR, "BotConstructChat: message %s variable %d out of range\n", message, num); + return qfalse; + } //end if + if (match->variables[num].offset >= 0) + { + assert( match->variables[num].offset >= 0 ); + ptr = &match->string[ (int) match->variables[num].offset]; + for (i = 0; i < match->variables[num].length; i++) + { + temp[i] = ptr[i]; + } //end for + temp[i] = 0; + //if it's a reply message + if (reply) + { + //replace the reply synonyms in the variables + BotReplaceReplySynonyms(temp, vcontext); + } //end if + else + { + //replace synonyms in the variable context + BotReplaceSynonyms(temp, vcontext); + } //end else + // + if (len + strlen(temp) >= MAX_MESSAGE_SIZE) + { + botimport.Print(PRT_ERROR, "BotConstructChat: message %s too long\n", message); + return qfalse; + } //end if + strcpy(&outputbuf[len], temp); + len += strlen(temp); + } //end if + break; + } //end case + case 'r': //random + { + msgptr++; + for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++) + { + temp[i] = *msgptr++; + } //end while + temp[i] = '\0'; + //step over the trailing escape char + if (*msgptr) msgptr++; + //find the random keyword + ptr = RandomString(temp); + if (!ptr) + { + botimport.Print(PRT_ERROR, "BotConstructChat: unknown random string %s\n", temp); + return qfalse; + } //end if + if (len + strlen(ptr) >= MAX_MESSAGE_SIZE) + { + botimport.Print(PRT_ERROR, "BotConstructChat: message \"%s\" too long\n", message); + return qfalse; + } //end if + strcpy(&outputbuf[len], ptr); + len += strlen(ptr); + expansion = qtrue; + break; + } //end case + default: + { + botimport.Print(PRT_FATAL, "BotConstructChat: message \"%s\" invalid escape char\n", message); + break; + } //end default + } //end switch + } //end if + else + { + outputbuf[len++] = *msgptr++; + if (len >= MAX_MESSAGE_SIZE) + { + botimport.Print(PRT_ERROR, "BotConstructChat: message \"%s\" too long\n", message); + break; + } //end if + } //end else + } //end while + outputbuf[len] = '\0'; + //replace synonyms weighted in the message context + BotReplaceWeightedSynonyms(outputbuf, mcontext); + //return true if a random was expanded + return expansion; +} //end of the function BotExpandChatMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotConstructChatMessage(bot_chatstate_t *chatstate, char *message, unsigned long mcontext, + bot_match_t *match, unsigned long vcontext, int reply) +{ + int i; + char srcmessage[MAX_MESSAGE_SIZE]; + + strcpy(srcmessage, message); + for (i = 0; i < 10; i++) + { + if (!BotExpandChatMessage(chatstate->chatmessage, srcmessage, mcontext, match, vcontext, reply)) + { + break; + } //end if + strcpy(srcmessage, chatstate->chatmessage); + } //end for + if (i >= 10) + { + botimport.Print(PRT_WARNING, "too many expansions in chat message\n"); + botimport.Print(PRT_WARNING, "%s\n", chatstate->chatmessage); + } //end if +} //end of the function BotConstructChatMessage +//=========================================================================== +// randomly chooses one of the chat message of the given type +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *BotChooseInitialChatMessage(bot_chatstate_t *cs, char *type) +{ + int n, numchatmessages; + float besttime; + bot_chattype_t *t; + bot_chatmessage_t *m, *bestchatmessage; + bot_chat_t *chat; + + chat = cs->chat; + for (t = chat->types; t; t = t->next) + { + if (!Q_stricmp(t->name, type)) + { + numchatmessages = 0; + for (m = t->firstchatmessage; m; m = m->next) + { + if (m->time > AAS_Time()) continue; + numchatmessages++; + } //end if + //if all chat messages have been used recently + if (numchatmessages <= 0) + { + besttime = 0; + bestchatmessage = NULL; + for (m = t->firstchatmessage; m; m = m->next) + { + if (!besttime || m->time < besttime) + { + bestchatmessage = m; + besttime = m->time; + } //end if + } //end for + if (bestchatmessage) return bestchatmessage->chatmessage; + } //end if + else //choose a chat message randomly + { + n = random() * numchatmessages; + for (m = t->firstchatmessage; m; m = m->next) + { + if (m->time > AAS_Time()) continue; + if (--n < 0) + { + m->time = AAS_Time() + CHATMESSAGE_RECENTTIME; + return m->chatmessage; + } //end if + } //end for + } //end else + return NULL; + } //end if + } //end for + return NULL; +} //end of the function BotChooseInitialChatMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotNumInitialChats(int chatstate, char *type) +{ + bot_chatstate_t *cs; + bot_chattype_t *t; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return 0; + + for (t = cs->chat->types; t; t = t->next) + { + if (!Q_stricmp(t->name, type)) + { + if (LibVarGetValue("bot_testichat")) { + botimport.Print(PRT_MESSAGE, "%s has %d chat lines\n", type, t->numchatmessages); + botimport.Print(PRT_MESSAGE, "-------------------\n"); + } + return t->numchatmessages; + } //end if + } //end for + return 0; +} //end of the function BotNumInitialChats +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7) +{ + char *message; + int index; + bot_match_t match; + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + //if no chat file is loaded + if (!cs->chat) return; + //choose a chat message randomly of the given type + message = BotChooseInitialChatMessage(cs, type); + //if there's no message of the given type + if (!message) + { +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "no chat messages of type %s\n", type); +#endif //DEBUG + return; + } //end if + // + Com_Memset(&match, 0, sizeof(match)); + index = 0; + if( var0 ) { + strcat(match.string, var0); + match.variables[0].offset = index; + match.variables[0].length = strlen(var0); + index += strlen(var0); + } + if( var1 ) { + strcat(match.string, var1); + match.variables[1].offset = index; + match.variables[1].length = strlen(var1); + index += strlen(var1); + } + if( var2 ) { + strcat(match.string, var2); + match.variables[2].offset = index; + match.variables[2].length = strlen(var2); + index += strlen(var2); + } + if( var3 ) { + strcat(match.string, var3); + match.variables[3].offset = index; + match.variables[3].length = strlen(var3); + index += strlen(var3); + } + if( var4 ) { + strcat(match.string, var4); + match.variables[4].offset = index; + match.variables[4].length = strlen(var4); + index += strlen(var4); + } + if( var5 ) { + strcat(match.string, var5); + match.variables[5].offset = index; + match.variables[5].length = strlen(var5); + index += strlen(var5); + } + if( var6 ) { + strcat(match.string, var6); + match.variables[6].offset = index; + match.variables[6].length = strlen(var6); + index += strlen(var6); + } + if( var7 ) { + strcat(match.string, var7); + match.variables[7].offset = index; + match.variables[7].length = strlen(var7); + index += strlen(var7); + } + // + BotConstructChatMessage(cs, message, mcontext, &match, 0, qfalse); +} //end of the function BotInitialChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotPrintReplyChatKeys(bot_replychat_t *replychat) +{ + bot_replychatkey_t *key; + bot_matchpiece_t *mp; + + botimport.Print(PRT_MESSAGE, "["); + for (key = replychat->keys; key; key = key->next) + { + if (key->flags & RCKFL_AND) botimport.Print(PRT_MESSAGE, "&"); + else if (key->flags & RCKFL_NOT) botimport.Print(PRT_MESSAGE, "!"); + // + if (key->flags & RCKFL_NAME) botimport.Print(PRT_MESSAGE, "name"); + else if (key->flags & RCKFL_GENDERFEMALE) botimport.Print(PRT_MESSAGE, "female"); + else if (key->flags & RCKFL_GENDERMALE) botimport.Print(PRT_MESSAGE, "male"); + else if (key->flags & RCKFL_GENDERLESS) botimport.Print(PRT_MESSAGE, "it"); + else if (key->flags & RCKFL_VARIABLES) + { + botimport.Print(PRT_MESSAGE, "("); + for (mp = key->match; mp; mp = mp->next) + { + if (mp->type == MT_STRING) botimport.Print(PRT_MESSAGE, "\"%s\"", mp->firststring->string); + else botimport.Print(PRT_MESSAGE, "%d", mp->variable); + if (mp->next) botimport.Print(PRT_MESSAGE, ", "); + } //end for + botimport.Print(PRT_MESSAGE, ")"); + } //end if + else if (key->flags & RCKFL_STRING) + { + botimport.Print(PRT_MESSAGE, "\"%s\"", key->string); + } //end if + if (key->next) botimport.Print(PRT_MESSAGE, ", "); + else botimport.Print(PRT_MESSAGE, "] = %1.0f\n", replychat->priority); + } //end for + botimport.Print(PRT_MESSAGE, "{\n"); +} //end of the function BotPrintReplyChatKeys +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotReplyChat(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7) +{ + bot_replychat_t *rchat, *bestrchat; + bot_replychatkey_t *key; + bot_chatmessage_t *m, *bestchatmessage; + bot_match_t match, bestmatch; + int bestpriority, num, found, res, numchatmessages, index; + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return qfalse; + Com_Memset(&match, 0, sizeof(bot_match_t)); + strcpy(match.string, message); + bestpriority = -1; + bestchatmessage = NULL; + bestrchat = NULL; + //go through all the reply chats + for (rchat = replychats; rchat; rchat = rchat->next) + { + found = qfalse; + for (key = rchat->keys; key; key = key->next) + { + res = qfalse; + //get the match result + if (key->flags & RCKFL_NAME) res = (StringContains(message, cs->name, qfalse) != -1); + else if (key->flags & RCKFL_BOTNAMES) res = (StringContains(key->string, cs->name, qfalse) != -1); + else if (key->flags & RCKFL_GENDERFEMALE) res = (cs->gender == CHAT_GENDERFEMALE); + else if (key->flags & RCKFL_GENDERMALE) res = (cs->gender == CHAT_GENDERMALE); + else if (key->flags & RCKFL_GENDERLESS) res = (cs->gender == CHAT_GENDERLESS); + else if (key->flags & RCKFL_VARIABLES) res = StringsMatch(key->match, &match); + else if (key->flags & RCKFL_STRING) res = (StringContainsWord(message, key->string, qfalse) != NULL); + //if the key must be present + if (key->flags & RCKFL_AND) + { + if (!res) + { + found = qfalse; + break; + } //end if + } //end else if + //if the key must be absent + else if (key->flags & RCKFL_NOT) + { + if (res) + { + found = qfalse; + break; + } //end if + } //end if + else if (res) + { + found = qtrue; + } //end else + } //end for + // + if (found) + { + if (rchat->priority > bestpriority) + { + numchatmessages = 0; + for (m = rchat->firstchatmessage; m; m = m->next) + { + if (m->time > AAS_Time()) continue; + numchatmessages++; + } //end if + num = random() * numchatmessages; + for (m = rchat->firstchatmessage; m; m = m->next) + { + if (--num < 0) break; + if (m->time > AAS_Time()) continue; + } //end for + //if the reply chat has a message + if (m) + { + Com_Memcpy(&bestmatch, &match, sizeof(bot_match_t)); + bestchatmessage = m; + bestrchat = rchat; + bestpriority = rchat->priority; + } //end if + } //end if + } //end if + } //end for + if (bestchatmessage) + { + index = strlen(bestmatch.string); + if( var0 ) { + strcat(bestmatch.string, var0); + bestmatch.variables[0].offset = index; + bestmatch.variables[0].length = strlen(var0); + index += strlen(var0); + } + if( var1 ) { + strcat(bestmatch.string, var1); + bestmatch.variables[1].offset = index; + bestmatch.variables[1].length = strlen(var1); + index += strlen(var1); + } + if( var2 ) { + strcat(bestmatch.string, var2); + bestmatch.variables[2].offset = index; + bestmatch.variables[2].length = strlen(var2); + index += strlen(var2); + } + if( var3 ) { + strcat(bestmatch.string, var3); + bestmatch.variables[3].offset = index; + bestmatch.variables[3].length = strlen(var3); + index += strlen(var3); + } + if( var4 ) { + strcat(bestmatch.string, var4); + bestmatch.variables[4].offset = index; + bestmatch.variables[4].length = strlen(var4); + index += strlen(var4); + } + if( var5 ) { + strcat(bestmatch.string, var5); + bestmatch.variables[5].offset = index; + bestmatch.variables[5].length = strlen(var5); + index += strlen(var5); + } + if( var6 ) { + strcat(bestmatch.string, var6); + bestmatch.variables[6].offset = index; + bestmatch.variables[6].length = strlen(var6); + index += strlen(var6); + } + if( var7 ) { + strcat(bestmatch.string, var7); + bestmatch.variables[7].offset = index; + bestmatch.variables[7].length = strlen(var7); + index += strlen(var7); + } + if (LibVarGetValue("bot_testrchat")) + { + for (m = bestrchat->firstchatmessage; m; m = m->next) + { + BotConstructChatMessage(cs, m->chatmessage, mcontext, &bestmatch, vcontext, qtrue); + BotRemoveTildes(cs->chatmessage); + botimport.Print(PRT_MESSAGE, "%s\n", cs->chatmessage); + } //end if + } //end if + else + { + bestchatmessage->time = AAS_Time() + CHATMESSAGE_RECENTTIME; + BotConstructChatMessage(cs, bestchatmessage->chatmessage, mcontext, &bestmatch, vcontext, qtrue); + } //end else + return qtrue; + } //end if + return qfalse; +} //end of the function BotReplyChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotChatLength(int chatstate) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return 0; + return strlen(cs->chatmessage); +} //end of the function BotChatLength +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotEnterChat(int chatstate, int clientto, int sendto) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + + if (strlen(cs->chatmessage)) + { + BotRemoveTildes(cs->chatmessage); + if (LibVarGetValue("bot_testichat")) { + botimport.Print(PRT_MESSAGE, "%s\n", cs->chatmessage); + } + else { + switch(sendto) { + case CHAT_TEAM: + EA_Command(cs->client, va("say_team %s", cs->chatmessage)); + break; + case CHAT_TELL: + EA_Command(cs->client, va("tell %d %s", clientto, cs->chatmessage)); + break; + default: //CHAT_ALL + EA_Command(cs->client, va("say %s", cs->chatmessage)); + break; + } + } + //clear the chat message from the state + strcpy(cs->chatmessage, ""); + } //end if +} //end of the function BotEnterChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotGetChatMessage(int chatstate, char *buf, int size) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + + BotRemoveTildes(cs->chatmessage); + strncpy(buf, cs->chatmessage, size-1); + buf[size-1] = '\0'; + //clear the chat message from the state + strcpy(cs->chatmessage, ""); +} //end of the function BotGetChatMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotSetChatGender(int chatstate, int gender) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + switch(gender) + { + case CHAT_GENDERFEMALE: cs->gender = CHAT_GENDERFEMALE; break; + case CHAT_GENDERMALE: cs->gender = CHAT_GENDERMALE; break; + default: cs->gender = CHAT_GENDERLESS; break; + } //end switch +} //end of the function BotSetChatGender +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotSetChatName(int chatstate, char *name, int client) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + cs->client = client; + Com_Memset(cs->name, 0, sizeof(cs->name)); + strncpy(cs->name, name, sizeof(cs->name)); + cs->name[sizeof(cs->name)-1] = '\0'; +} //end of the function BotSetChatName +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetChatAI(void) +{ + bot_replychat_t *rchat; + bot_chatmessage_t *m; + + for (rchat = replychats; rchat; rchat = rchat->next) + { + for (m = rchat->firstchatmessage; m; m = m->next) + { + m->time = 0; + } //end for + } //end for +} //end of the function BotResetChatAI +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +int BotAllocChatState(void) +{ + int i; + + for (i = 1; i <= MAX_CLIENTS; i++) + { + if (!botchatstates[i]) + { + botchatstates[i] = GetClearedMemory(sizeof(bot_chatstate_t)); + return i; + } //end if + } //end for + return 0; +} //end of the function BotAllocChatState +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeChatState(int handle) +{ + bot_chatstate_t *cs; + bot_consolemessage_t m; + int h; + + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "chat state handle %d out of range\n", handle); + return; + } //end if + if (!botchatstates[handle]) + { + botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle); + return; + } //end if + cs = botchatstates[handle]; + if (LibVarGetValue("bot_reloadcharacters")) + { + BotFreeChatFile(handle); + } //end if + //free all the console messages left in the chat state + for (h = BotNextConsoleMessage(handle, &m); h; h = BotNextConsoleMessage(handle, &m)) + { + //remove the console message + BotRemoveConsoleMessage(handle, h); + } //end for + FreeMemory(botchatstates[handle]); + botchatstates[handle] = NULL; +} //end of the function BotFreeChatState +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotSetupChatAI(void) +{ + char *file; + +#ifdef DEBUG + int starttime = Sys_MilliSeconds(); +#endif //DEBUG + + file = LibVarString("synfile", "syn.c"); + synonyms = BotLoadSynonyms(file); + file = LibVarString("rndfile", "rnd.c"); + randomstrings = BotLoadRandomStrings(file); + file = LibVarString("matchfile", "match.c"); + matchtemplates = BotLoadMatchTemplates(file); + // + if (!LibVarValue("nochat", "0")) + { + file = LibVarString("rchatfile", "rchat.c"); + replychats = BotLoadReplyChat(file); + } //end if + + InitConsoleMessageHeap(); + +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "setup chat AI %d msec\n", Sys_MilliSeconds() - starttime); +#endif //DEBUG + return BLERR_NOERROR; +} //end of the function BotSetupChatAI +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotShutdownChatAI(void) +{ + int i; + + //free all remaining chat states + for(i = 0; i < MAX_CLIENTS; i++) + { + if (botchatstates[i]) + { + BotFreeChatState(i); + } //end if + } //end for + //free all cached chats + for(i = 0; i < MAX_CLIENTS; i++) + { + if (ichatdata[i]) + { + FreeMemory(ichatdata[i]->chat); + FreeMemory(ichatdata[i]); + ichatdata[i] = NULL; + } //end if + } //end for + if (consolemessageheap) FreeMemory(consolemessageheap); + consolemessageheap = NULL; + if (matchtemplates) BotFreeMatchTemplates(matchtemplates); + matchtemplates = NULL; + if (randomstrings) FreeMemory(randomstrings); + randomstrings = NULL; + if (synonyms) FreeMemory(synonyms); + synonyms = NULL; + if (replychats) BotFreeReplyChat(replychats); + replychats = NULL; +} //end of the function BotShutdownChatAI diff --git a/reaction/engine/code/botlib/be_ai_chat.h b/reaction/engine/code/botlib/be_ai_chat.h new file mode 100644 index 00000000..53a56d7b --- /dev/null +++ b/reaction/engine/code/botlib/be_ai_chat.h @@ -0,0 +1,113 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +/***************************************************************************** + * name: be_ai_chat.h + * + * desc: char AI + * + * $Archive: /source/code/botlib/be_ai_chat.h $ + * + *****************************************************************************/ + +#define MAX_MESSAGE_SIZE 256 +#define MAX_CHATTYPE_NAME 32 +#define MAX_MATCHVARIABLES 8 + +#define CHAT_GENDERLESS 0 +#define CHAT_GENDERFEMALE 1 +#define CHAT_GENDERMALE 2 + +#define CHAT_ALL 0 +#define CHAT_TEAM 1 +#define CHAT_TELL 2 + +//a console message +typedef struct bot_consolemessage_s +{ + int handle; + float time; //message time + int type; //message type + char message[MAX_MESSAGE_SIZE]; //message + struct bot_consolemessage_s *prev, *next; //prev and next in list +} bot_consolemessage_t; + +//match variable +typedef struct bot_matchvariable_s +{ + char offset; + int length; +} bot_matchvariable_t; +//returned to AI when a match is found +typedef struct bot_match_s +{ + char string[MAX_MESSAGE_SIZE]; + int type; + int subtype; + bot_matchvariable_t variables[MAX_MATCHVARIABLES]; +} bot_match_t; + +//setup the chat AI +int BotSetupChatAI(void); +//shutdown the chat AI +void BotShutdownChatAI(void); +//returns the handle to a newly allocated chat state +int BotAllocChatState(void); +//frees the chatstate +void BotFreeChatState(int handle); +//adds a console message to the chat state +void BotQueueConsoleMessage(int chatstate, int type, char *message); +//removes the console message from the chat state +void BotRemoveConsoleMessage(int chatstate, int handle); +//returns the next console message from the state +int BotNextConsoleMessage(int chatstate, bot_consolemessage_t *cm); +//returns the number of console messages currently stored in the state +int BotNumConsoleMessages(int chatstate); +//selects a chat message of the given type +void BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); +//returns the number of initial chat messages of the given type +int BotNumInitialChats(int chatstate, char *type); +//find and select a reply for the given message +int BotReplyChat(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); +//returns the length of the currently selected chat message +int BotChatLength(int chatstate); +//enters the selected chat message +void BotEnterChat(int chatstate, int clientto, int sendto); +//get the chat message ready to be output +void BotGetChatMessage(int chatstate, char *buf, int size); +//checks if the first string contains the second one, returns index into first string or -1 if not found +int StringContains(char *str1, char *str2, int casesensitive); +//finds a match for the given string using the match templates +int BotFindMatch(char *str, bot_match_t *match, unsigned long int context); +//returns a variable from a match +void BotMatchVariable(bot_match_t *match, int variable, char *buf, int size); +//unify all the white spaces in the string +void UnifyWhiteSpaces(char *string); +//replace all the context related synonyms in the string +void BotReplaceSynonyms(char *string, unsigned long int context); +//loads a chat file for the chat state +int BotLoadChatFile(int chatstate, char *chatfile, char *chatname); +//store the gender of the bot in the chat state +void BotSetChatGender(int chatstate, int gender); +//store the bot name in the chat state +void BotSetChatName(int chatstate, char *name, int client); + diff --git a/reaction/engine/code/botlib/be_ai_gen.c b/reaction/engine/code/botlib/be_ai_gen.c new file mode 100644 index 00000000..d7bb2214 --- /dev/null +++ b/reaction/engine/code/botlib/be_ai_gen.c @@ -0,0 +1,134 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_gen.c + * + * desc: genetic selection + * + * $Archive: /MissionPack/code/botlib/be_ai_gen.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_log.h" +#include "l_utils.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_ai_gen.h" + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int GeneticSelection(int numranks, float *rankings) +{ + float sum, select; + int i, index; + + sum = 0; + for (i = 0; i < numranks; i++) + { + if (rankings[i] < 0) continue; + sum += rankings[i]; + } //end for + if (sum > 0) + { + //select a bot where the ones with the higest rankings have + //the highest chance of being selected + select = random() * sum; + for (i = 0; i < numranks; i++) + { + if (rankings[i] < 0) continue; + sum -= rankings[i]; + if (sum <= 0) return i; + } //end for + } //end if + //select a bot randomly + index = random() * numranks; + for (i = 0; i < numranks; i++) + { + if (rankings[index] >= 0) return index; + index = (index + 1) % numranks; + } //end for + return 0; +} //end of the function GeneticSelection +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int GeneticParentsAndChildSelection(int numranks, float *ranks, int *parent1, int *parent2, int *child) +{ + float rankings[256], max; + int i; + + if (numranks > 256) + { + botimport.Print(PRT_WARNING, "GeneticParentsAndChildSelection: too many bots\n"); + *parent1 = *parent2 = *child = 0; + return qfalse; + } //end if + for (max = 0, i = 0; i < numranks; i++) + { + if (ranks[i] < 0) continue; + max++; + } //end for + if (max < 3) + { + botimport.Print(PRT_WARNING, "GeneticParentsAndChildSelection: too few valid bots\n"); + *parent1 = *parent2 = *child = 0; + return qfalse; + } //end if + Com_Memcpy(rankings, ranks, sizeof(float) * numranks); + //select first parent + *parent1 = GeneticSelection(numranks, rankings); + rankings[*parent1] = -1; + //select second parent + *parent2 = GeneticSelection(numranks, rankings); + rankings[*parent2] = -1; + //reverse the rankings + max = 0; + for (i = 0; i < numranks; i++) + { + if (rankings[i] < 0) continue; + if (rankings[i] > max) max = rankings[i]; + } //end for + for (i = 0; i < numranks; i++) + { + if (rankings[i] < 0) continue; + rankings[i] = max - rankings[i]; + } //end for + //select child + *child = GeneticSelection(numranks, rankings); + return qtrue; +} //end of the function GeneticParentsAndChildSelection diff --git a/reaction/engine/code/botlib/be_ai_gen.h b/reaction/engine/code/botlib/be_ai_gen.h new file mode 100644 index 00000000..ce9ba92f --- /dev/null +++ b/reaction/engine/code/botlib/be_ai_gen.h @@ -0,0 +1,33 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: be_ai_gen.h + * + * desc: genetic selection + * + * $Archive: /source/code/botlib/be_ai_gen.h $ + * + *****************************************************************************/ + +int GeneticParentsAndChildSelection(int numranks, float *ranks, int *parent1, int *parent2, int *child); diff --git a/reaction/engine/code/botlib/be_ai_goal.c b/reaction/engine/code/botlib/be_ai_goal.c new file mode 100644 index 00000000..fbcc20fd --- /dev/null +++ b/reaction/engine/code/botlib/be_ai_goal.c @@ -0,0 +1,1821 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_goal.c + * + * desc: goal AI + * + * $Archive: /MissionPack/code/botlib/be_ai_goal.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_utils.h" +#include "l_libvar.h" +#include "l_memory.h" +#include "l_log.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_ai_weight.h" +#include "be_ai_goal.h" +#include "be_ai_move.h" + +//#define DEBUG_AI_GOAL +#ifdef RANDOMIZE +#define UNDECIDEDFUZZY +#endif //RANDOMIZE +#define DROPPEDWEIGHT +//minimum avoid goal time +#define AVOID_MINIMUM_TIME 10 +//default avoid goal time +#define AVOID_DEFAULT_TIME 30 +//avoid dropped goal time +#define AVOID_DROPPED_TIME 10 +// +#define TRAVELTIME_SCALE 0.01 +//item flags +#define IFL_NOTFREE 1 //not in free for all +#define IFL_NOTTEAM 2 //not in team play +#define IFL_NOTSINGLE 4 //not in single player +#define IFL_NOTBOT 8 //bot should never go for this +#define IFL_ROAM 16 //bot roam goal + +//location in the map "target_location" +typedef struct maplocation_s +{ + vec3_t origin; + int areanum; + char name[MAX_EPAIRKEY]; + struct maplocation_s *next; +} maplocation_t; + +//camp spots "info_camp" +typedef struct campspot_s +{ + vec3_t origin; + int areanum; + char name[MAX_EPAIRKEY]; + float range; + float weight; + float wait; + float random; + struct campspot_s *next; +} campspot_t; + +//FIXME: these are game specific +typedef enum { + GT_FFA, // free for all + GT_TOURNAMENT, // one on one tournament + GT_SINGLE_PLAYER, // single player tournament + + //-- team games go after this -- + + GT_TEAM, // team deathmatch + GT_CTF, // capture the flag +#ifdef MISSIONPACK + GT_1FCTF, + GT_OBELISK, + GT_HARVESTER, +#endif + GT_MAX_GAME_TYPE +} gametype_t; + +typedef struct levelitem_s +{ + int number; //number of the level item + int iteminfo; //index into the item info + int flags; //item flags + float weight; //fixed roam weight + vec3_t origin; //origin of the item + int goalareanum; //area the item is in + vec3_t goalorigin; //goal origin within the area + int entitynum; //entity number + float timeout; //item is removed after this time + struct levelitem_s *prev, *next; +} levelitem_t; + +typedef struct iteminfo_s +{ + char classname[32]; //classname of the item + char name[MAX_STRINGFIELD]; //name of the item + char model[MAX_STRINGFIELD]; //model of the item + int modelindex; //model index + int type; //item type + int index; //index in the inventory + float respawntime; //respawn time + vec3_t mins; //mins of the item + vec3_t maxs; //maxs of the item + int number; //number of the item info +} iteminfo_t; + +#define ITEMINFO_OFS(x) (size_t)&(((iteminfo_t *)0)->x) + +fielddef_t iteminfo_fields[] = +{ +{"name", ITEMINFO_OFS(name), FT_STRING}, +{"model", ITEMINFO_OFS(model), FT_STRING}, +{"modelindex", ITEMINFO_OFS(modelindex), FT_INT}, +{"type", ITEMINFO_OFS(type), FT_INT}, +{"index", ITEMINFO_OFS(index), FT_INT}, +{"respawntime", ITEMINFO_OFS(respawntime), FT_FLOAT}, +{"mins", ITEMINFO_OFS(mins), FT_FLOAT|FT_ARRAY, 3}, +{"maxs", ITEMINFO_OFS(maxs), FT_FLOAT|FT_ARRAY, 3}, +{NULL, 0, 0} +}; + +structdef_t iteminfo_struct = +{ + sizeof(iteminfo_t), iteminfo_fields +}; + +typedef struct itemconfig_s +{ + int numiteminfo; + iteminfo_t *iteminfo; +} itemconfig_t; + +//goal state +typedef struct bot_goalstate_s +{ + struct weightconfig_s *itemweightconfig; //weight config + int *itemweightindex; //index from item to weight + // + int client; //client using this goal state + int lastreachabilityarea; //last area with reachabilities the bot was in + // + bot_goal_t goalstack[MAX_GOALSTACK]; //goal stack + int goalstacktop; //the top of the goal stack + // + int avoidgoals[MAX_AVOIDGOALS]; //goals to avoid + float avoidgoaltimes[MAX_AVOIDGOALS]; //times to avoid the goals +} bot_goalstate_t; + +bot_goalstate_t *botgoalstates[MAX_CLIENTS + 1]; // FIXME: init? +//item configuration +itemconfig_t *itemconfig = NULL; +//level items +levelitem_t *levelitemheap = NULL; +levelitem_t *freelevelitems = NULL; +levelitem_t *levelitems = NULL; +int numlevelitems = 0; +//map locations +maplocation_t *maplocations = NULL; +//camp spots +campspot_t *campspots = NULL; +//the game type +int g_gametype = 0; +//additional dropped item weight +libvar_t *droppedweight = NULL; + +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +bot_goalstate_t *BotGoalStateFromHandle(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "goal state handle %d out of range\n", handle); + return NULL; + } //end if + if (!botgoalstates[handle]) + { + botimport.Print(PRT_FATAL, "invalid goal state %d\n", handle); + return NULL; + } //end if + return botgoalstates[handle]; +} //end of the function BotGoalStateFromHandle +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child) +{ + bot_goalstate_t *p1, *p2, *c; + + p1 = BotGoalStateFromHandle(parent1); + p2 = BotGoalStateFromHandle(parent2); + c = BotGoalStateFromHandle(child); + + InterbreedWeightConfigs(p1->itemweightconfig, p2->itemweightconfig, + c->itemweightconfig); +} //end of the function BotInterbreedingGoalFuzzyLogic +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotSaveGoalFuzzyLogic(int goalstate, char *filename) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + + //WriteWeightConfig(filename, gs->itemweightconfig); +} //end of the function BotSaveGoalFuzzyLogic +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotMutateGoalFuzzyLogic(int goalstate, float range) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + + EvolveWeightConfig(gs->itemweightconfig); +} //end of the function BotMutateGoalFuzzyLogic +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +itemconfig_t *LoadItemConfig(char *filename) +{ + int max_iteminfo; + token_t token; + char path[MAX_PATH]; + source_t *source; + itemconfig_t *ic; + iteminfo_t *ii; + + max_iteminfo = (int) LibVarValue("max_iteminfo", "256"); + if (max_iteminfo < 0) + { + botimport.Print(PRT_ERROR, "max_iteminfo = %d\n", max_iteminfo); + max_iteminfo = 256; + LibVarSet( "max_iteminfo", "256" ); + } + + strncpy( path, filename, MAX_PATH ); + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile( path ); + if( !source ) { + botimport.Print( PRT_ERROR, "counldn't load %s\n", path ); + return NULL; + } //end if + //initialize item config + ic = (itemconfig_t *) GetClearedHunkMemory(sizeof(itemconfig_t) + + max_iteminfo * sizeof(iteminfo_t)); + ic->iteminfo = (iteminfo_t *) ((char *) ic + sizeof(itemconfig_t)); + ic->numiteminfo = 0; + //parse the item config file + while(PC_ReadToken(source, &token)) + { + if (!strcmp(token.string, "iteminfo")) + { + if (ic->numiteminfo >= max_iteminfo) + { + SourceError(source, "more than %d item info defined\n", max_iteminfo); + FreeMemory(ic); + FreeSource(source); + return NULL; + } //end if + ii = &ic->iteminfo[ic->numiteminfo]; + Com_Memset(ii, 0, sizeof(iteminfo_t)); + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) + { + FreeMemory(ic); + FreeMemory(source); + return NULL; + } //end if + StripDoubleQuotes(token.string); + strncpy(ii->classname, token.string, sizeof(ii->classname)-1); + if (!ReadStructure(source, &iteminfo_struct, (char *) ii)) + { + FreeMemory(ic); + FreeSource(source); + return NULL; + } //end if + ii->number = ic->numiteminfo; + ic->numiteminfo++; + } //end if + else + { + SourceError(source, "unknown definition %s\n", token.string); + FreeMemory(ic); + FreeSource(source); + return NULL; + } //end else + } //end while + FreeSource(source); + // + if (!ic->numiteminfo) botimport.Print(PRT_WARNING, "no item info loaded\n"); + botimport.Print(PRT_MESSAGE, "loaded %s\n", path); + return ic; +} //end of the function LoadItemConfig +//=========================================================================== +// index to find the weight function of an iteminfo +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int *ItemWeightIndex(weightconfig_t *iwc, itemconfig_t *ic) +{ + int *index, i; + + //initialize item weight index + index = (int *) GetClearedMemory(sizeof(int) * ic->numiteminfo); + + for (i = 0; i < ic->numiteminfo; i++) + { + index[i] = FindFuzzyWeight(iwc, ic->iteminfo[i].classname); + if (index[i] < 0) + { + Log_Write("item info %d \"%s\" has no fuzzy weight\r\n", i, ic->iteminfo[i].classname); + } //end if + } //end for + return index; +} //end of the function ItemWeightIndex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void InitLevelItemHeap(void) +{ + int i, max_levelitems; + + if (levelitemheap) FreeMemory(levelitemheap); + + max_levelitems = (int) LibVarValue("max_levelitems", "256"); + levelitemheap = (levelitem_t *) GetClearedMemory(max_levelitems * sizeof(levelitem_t)); + + for (i = 0; i < max_levelitems-1; i++) + { + levelitemheap[i].next = &levelitemheap[i + 1]; + } //end for + levelitemheap[max_levelitems-1].next = NULL; + // + freelevelitems = levelitemheap; +} //end of the function InitLevelItemHeap +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +levelitem_t *AllocLevelItem(void) +{ + levelitem_t *li; + + li = freelevelitems; + if (!li) + { + botimport.Print(PRT_FATAL, "out of level items\n"); + return NULL; + } //end if + // + freelevelitems = freelevelitems->next; + Com_Memset(li, 0, sizeof(levelitem_t)); + return li; +} //end of the function AllocLevelItem +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeLevelItem(levelitem_t *li) +{ + li->next = freelevelitems; + freelevelitems = li; +} //end of the function FreeLevelItem +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AddLevelItemToList(levelitem_t *li) +{ + if (levelitems) levelitems->prev = li; + li->prev = NULL; + li->next = levelitems; + levelitems = li; +} //end of the function AddLevelItemToList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void RemoveLevelItemFromList(levelitem_t *li) +{ + if (li->prev) li->prev->next = li->next; + else levelitems = li->next; + if (li->next) li->next->prev = li->prev; +} //end of the function RemoveLevelItemFromList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFreeInfoEntities(void) +{ + maplocation_t *ml, *nextml; + campspot_t *cs, *nextcs; + + for (ml = maplocations; ml; ml = nextml) + { + nextml = ml->next; + FreeMemory(ml); + } //end for + maplocations = NULL; + for (cs = campspots; cs; cs = nextcs) + { + nextcs = cs->next; + FreeMemory(cs); + } //end for + campspots = NULL; +} //end of the function BotFreeInfoEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotInitInfoEntities(void) +{ + char classname[MAX_EPAIRKEY]; + maplocation_t *ml; + campspot_t *cs; + int ent, numlocations, numcampspots; + + BotFreeInfoEntities(); + // + numlocations = 0; + numcampspots = 0; + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + + //map locations + if (!strcmp(classname, "target_location")) + { + ml = (maplocation_t *) GetClearedMemory(sizeof(maplocation_t)); + AAS_VectorForBSPEpairKey(ent, "origin", ml->origin); + AAS_ValueForBSPEpairKey(ent, "message", ml->name, sizeof(ml->name)); + ml->areanum = AAS_PointAreaNum(ml->origin); + ml->next = maplocations; + maplocations = ml; + numlocations++; + } //end if + //camp spots + else if (!strcmp(classname, "info_camp")) + { + cs = (campspot_t *) GetClearedMemory(sizeof(campspot_t)); + AAS_VectorForBSPEpairKey(ent, "origin", cs->origin); + //cs->origin[2] += 16; + AAS_ValueForBSPEpairKey(ent, "message", cs->name, sizeof(cs->name)); + AAS_FloatForBSPEpairKey(ent, "range", &cs->range); + AAS_FloatForBSPEpairKey(ent, "weight", &cs->weight); + AAS_FloatForBSPEpairKey(ent, "wait", &cs->wait); + AAS_FloatForBSPEpairKey(ent, "random", &cs->random); + cs->areanum = AAS_PointAreaNum(cs->origin); + if (!cs->areanum) + { + botimport.Print(PRT_MESSAGE, "camp spot at %1.1f %1.1f %1.1f in solid\n", cs->origin[0], cs->origin[1], cs->origin[2]); + FreeMemory(cs); + continue; + } //end if + cs->next = campspots; + campspots = cs; + //AAS_DrawPermanentCross(cs->origin, 4, LINECOLOR_YELLOW); + numcampspots++; + } //end else if + } //end for + if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "%d map locations\n", numlocations); + botimport.Print(PRT_MESSAGE, "%d camp spots\n", numcampspots); + } //end if +} //end of the function BotInitInfoEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotInitLevelItems(void) +{ + int i, spawnflags, value; + char classname[MAX_EPAIRKEY]; + vec3_t origin, end; + int ent, goalareanum; + itemconfig_t *ic; + levelitem_t *li; + bsp_trace_t trace; + + //initialize the map locations and camp spots + BotInitInfoEntities(); + + //initialize the level item heap + InitLevelItemHeap(); + levelitems = NULL; + numlevelitems = 0; + // + ic = itemconfig; + if (!ic) return; + + //if there's no AAS file loaded + if (!AAS_Loaded()) return; + + //update the modelindexes of the item info + for (i = 0; i < ic->numiteminfo; i++) + { + //ic->iteminfo[i].modelindex = AAS_IndexFromModel(ic->iteminfo[i].model); + if (!ic->iteminfo[i].modelindex) + { + Log_Write("item %s has modelindex 0", ic->iteminfo[i].classname); + } //end if + } //end for + + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + // + spawnflags = 0; + AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); + // + for (i = 0; i < ic->numiteminfo; i++) + { + if (!strcmp(classname, ic->iteminfo[i].classname)) break; + } //end for + if (i >= ic->numiteminfo) + { + Log_Write("entity %s unknown item\r\n", classname); + continue; + } //end if + //get the origin of the item + if (!AAS_VectorForBSPEpairKey(ent, "origin", origin)) + { + botimport.Print(PRT_ERROR, "item %s without origin\n", classname); + continue; + } //end else + // + goalareanum = 0; + //if it is a floating item + if (spawnflags & 1) + { + //if the item is not floating in water + if (!(AAS_PointContents(origin) & CONTENTS_WATER)) + { + VectorCopy(origin, end); + end[2] -= 32; + trace = AAS_Trace(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs, end, -1, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); + //if the item not near the ground + if (trace.fraction >= 1) + { + //if the item is not reachable from a jumppad + goalareanum = AAS_BestReachableFromJumpPadArea(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs); + Log_Write("item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum); + //botimport.Print(PRT_MESSAGE, "item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum); + if (!goalareanum) continue; + } //end if + } //end if + } //end if + + li = AllocLevelItem(); + if (!li) return; + // + li->number = ++numlevelitems; + li->timeout = 0; + li->entitynum = 0; + // + li->flags = 0; + AAS_IntForBSPEpairKey(ent, "notfree", &value); + if (value) li->flags |= IFL_NOTFREE; + AAS_IntForBSPEpairKey(ent, "notteam", &value); + if (value) li->flags |= IFL_NOTTEAM; + AAS_IntForBSPEpairKey(ent, "notsingle", &value); + if (value) li->flags |= IFL_NOTSINGLE; + AAS_IntForBSPEpairKey(ent, "notbot", &value); + if (value) li->flags |= IFL_NOTBOT; + if (!strcmp(classname, "item_botroam")) + { + li->flags |= IFL_ROAM; + AAS_FloatForBSPEpairKey(ent, "weight", &li->weight); + } //end if + //if not a stationary item + if (!(spawnflags & 1)) + { + if (!AAS_DropToFloor(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs)) + { + botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n", + classname, origin[0], origin[1], origin[2]); + } //end if + } //end if + //item info of the level item + li->iteminfo = i; + //origin of the item + VectorCopy(origin, li->origin); + // + if (goalareanum) + { + li->goalareanum = goalareanum; + VectorCopy(origin, li->goalorigin); + } //end if + else + { + //get the item goal area and goal origin + li->goalareanum = AAS_BestReachableArea(origin, + ic->iteminfo[i].mins, ic->iteminfo[i].maxs, + li->goalorigin); + if (!li->goalareanum) + { + botimport.Print(PRT_MESSAGE, "%s not reachable for bots at (%1.1f %1.1f %1.1f)\n", + classname, origin[0], origin[1], origin[2]); + } //end if + } //end else + // + AddLevelItemToList(li); + } //end for + botimport.Print(PRT_MESSAGE, "found %d level items\n", numlevelitems); +} //end of the function BotInitLevelItems +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotGoalName(int number, char *name, int size) +{ + levelitem_t *li; + + if (!itemconfig) return; + // + for (li = levelitems; li; li = li->next) + { + if (li->number == number) + { + strncpy(name, itemconfig->iteminfo[li->iteminfo].name, size-1); + name[size-1] = '\0'; + return; + } //end for + } //end for + strcpy(name, ""); + return; +} //end of the function BotGoalName +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetAvoidGoals(int goalstate) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + Com_Memset(gs->avoidgoals, 0, MAX_AVOIDGOALS * sizeof(int)); + Com_Memset(gs->avoidgoaltimes, 0, MAX_AVOIDGOALS * sizeof(float)); +} //end of the function BotResetAvoidGoals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpAvoidGoals(int goalstate) +{ + int i; + bot_goalstate_t *gs; + char name[32]; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + for (i = 0; i < MAX_AVOIDGOALS; i++) + { + if (gs->avoidgoaltimes[i] >= AAS_Time()) + { + BotGoalName(gs->avoidgoals[i], name, 32); + Log_Write("avoid goal %s, number %d for %f seconds", name, + gs->avoidgoals[i], gs->avoidgoaltimes[i] - AAS_Time()); + } //end if + } //end for +} //end of the function BotDumpAvoidGoals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotAddToAvoidGoals(bot_goalstate_t *gs, int number, float avoidtime) +{ + int i; + + for (i = 0; i < MAX_AVOIDGOALS; i++) + { + //if the avoid goal is already stored + if (gs->avoidgoals[i] == number) + { + gs->avoidgoals[i] = number; + gs->avoidgoaltimes[i] = AAS_Time() + avoidtime; + return; + } //end if + } //end for + + for (i = 0; i < MAX_AVOIDGOALS; i++) + { + //if this avoid goal has expired + if (gs->avoidgoaltimes[i] < AAS_Time()) + { + gs->avoidgoals[i] = number; + gs->avoidgoaltimes[i] = AAS_Time() + avoidtime; + return; + } //end if + } //end for +} //end of the function BotAddToAvoidGoals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotRemoveFromAvoidGoals(int goalstate, int number) +{ + int i; + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + //don't use the goals the bot wants to avoid + for (i = 0; i < MAX_AVOIDGOALS; i++) + { + if (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time()) + { + gs->avoidgoaltimes[i] = 0; + return; + } //end if + } //end for +} //end of the function BotRemoveFromAvoidGoals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float BotAvoidGoalTime(int goalstate, int number) +{ + int i; + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return 0; + //don't use the goals the bot wants to avoid + for (i = 0; i < MAX_AVOIDGOALS; i++) + { + if (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time()) + { + return gs->avoidgoaltimes[i] - AAS_Time(); + } //end if + } //end for + return 0; +} //end of the function BotAvoidGoalTime +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotSetAvoidGoalTime(int goalstate, int number, float avoidtime) +{ + bot_goalstate_t *gs; + levelitem_t *li; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) + return; + if (avoidtime < 0) + { + if (!itemconfig) + return; + // + for (li = levelitems; li; li = li->next) + { + if (li->number == number) + { + avoidtime = itemconfig->iteminfo[li->iteminfo].respawntime; + if (!avoidtime) + avoidtime = AVOID_DEFAULT_TIME; + if (avoidtime < AVOID_MINIMUM_TIME) + avoidtime = AVOID_MINIMUM_TIME; + BotAddToAvoidGoals(gs, number, avoidtime); + return; + } //end for + } //end for + return; + } //end if + else + { + BotAddToAvoidGoals(gs, number, avoidtime); + } //end else +} //end of the function BotSetAvoidGoalTime +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotGetLevelItemGoal(int index, char *name, bot_goal_t *goal) +{ + levelitem_t *li; + + if (!itemconfig) return -1; + li = levelitems; + if (index >= 0) + { + for (; li; li = li->next) + { + if (li->number == index) + { + li = li->next; + break; + } //end if + } //end for + } //end for + for (; li; li = li->next) + { + // + if (g_gametype == GT_SINGLE_PLAYER) { + if (li->flags & IFL_NOTSINGLE) continue; + } + else if (g_gametype >= GT_TEAM) { + if (li->flags & IFL_NOTTEAM) continue; + } + else { + if (li->flags & IFL_NOTFREE) continue; + } + if (li->flags & IFL_NOTBOT) continue; + // + if (!Q_stricmp(name, itemconfig->iteminfo[li->iteminfo].name)) + { + goal->areanum = li->goalareanum; + VectorCopy(li->goalorigin, goal->origin); + goal->entitynum = li->entitynum; + VectorCopy(itemconfig->iteminfo[li->iteminfo].mins, goal->mins); + VectorCopy(itemconfig->iteminfo[li->iteminfo].maxs, goal->maxs); + goal->number = li->number; + goal->flags = GFL_ITEM; + if (li->timeout) goal->flags |= GFL_DROPPED; + //botimport.Print(PRT_MESSAGE, "found li %s\n", itemconfig->iteminfo[li->iteminfo].name); + return li->number; + } //end if + } //end for + return -1; +} //end of the function BotGetLevelItemGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotGetMapLocationGoal(char *name, bot_goal_t *goal) +{ + maplocation_t *ml; + vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8}; + + for (ml = maplocations; ml; ml = ml->next) + { + if (!Q_stricmp(ml->name, name)) + { + goal->areanum = ml->areanum; + VectorCopy(ml->origin, goal->origin); + goal->entitynum = 0; + VectorCopy(mins, goal->mins); + VectorCopy(maxs, goal->maxs); + return qtrue; + } //end if + } //end for + return qfalse; +} //end of the function BotGetMapLocationGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotGetNextCampSpotGoal(int num, bot_goal_t *goal) +{ + int i; + campspot_t *cs; + vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8}; + + if (num < 0) num = 0; + i = num; + for (cs = campspots; cs; cs = cs->next) + { + if (--i < 0) + { + goal->areanum = cs->areanum; + VectorCopy(cs->origin, goal->origin); + goal->entitynum = 0; + VectorCopy(mins, goal->mins); + VectorCopy(maxs, goal->maxs); + return num+1; + } //end if + } //end for + return 0; +} //end of the function BotGetNextCampSpotGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFindEntityForLevelItem(levelitem_t *li) +{ + int ent, modelindex; + itemconfig_t *ic; + aas_entityinfo_t entinfo; + vec3_t dir; + + ic = itemconfig; + if (!itemconfig) return; + for (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent)) + { + //get the model index of the entity + modelindex = AAS_EntityModelindex(ent); + // + if (!modelindex) continue; + //get info about the entity + AAS_EntityInfo(ent, &entinfo); + //if the entity is still moving + if (entinfo.origin[0] != entinfo.lastvisorigin[0] || + entinfo.origin[1] != entinfo.lastvisorigin[1] || + entinfo.origin[2] != entinfo.lastvisorigin[2]) continue; + // + if (ic->iteminfo[li->iteminfo].modelindex == modelindex) + { + //check if the entity is very close + VectorSubtract(li->origin, entinfo.origin, dir); + if (VectorLength(dir) < 30) + { + //found an entity for this level item + li->entitynum = ent; + } //end if + } //end if + } //end for +} //end of the function BotFindEntityForLevelItem +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +//NOTE: enum entityType_t in bg_public.h +#define ET_ITEM 2 + +void BotUpdateEntityItems(void) +{ + int ent, i, modelindex; + vec3_t dir; + levelitem_t *li, *nextli; + aas_entityinfo_t entinfo; + itemconfig_t *ic; + + //timeout current entity items if necessary + for (li = levelitems; li; li = nextli) + { + nextli = li->next; + //if it is a item that will time out + if (li->timeout) + { + //timeout the item + if (li->timeout < AAS_Time()) + { + RemoveLevelItemFromList(li); + FreeLevelItem(li); + } //end if + } //end if + } //end for + //find new entity items + ic = itemconfig; + if (!itemconfig) return; + // + for (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent)) + { + if (AAS_EntityType(ent) != ET_ITEM) continue; + //get the model index of the entity + modelindex = AAS_EntityModelindex(ent); + // + if (!modelindex) continue; + //get info about the entity + AAS_EntityInfo(ent, &entinfo); + //FIXME: don't do this + //skip all floating items for now + //if (entinfo.groundent != ENTITYNUM_WORLD) continue; + //if the entity is still moving + if (entinfo.origin[0] != entinfo.lastvisorigin[0] || + entinfo.origin[1] != entinfo.lastvisorigin[1] || + entinfo.origin[2] != entinfo.lastvisorigin[2]) continue; + //check if the entity is already stored as a level item + for (li = levelitems; li; li = li->next) + { + //if the level item is linked to an entity + if (li->entitynum && li->entitynum == ent) + { + //the entity is re-used if the models are different + if (ic->iteminfo[li->iteminfo].modelindex != modelindex) + { + //remove this level item + RemoveLevelItemFromList(li); + FreeLevelItem(li); + li = NULL; + break; + } //end if + else + { + if (entinfo.origin[0] != li->origin[0] || + entinfo.origin[1] != li->origin[1] || + entinfo.origin[2] != li->origin[2]) + { + VectorCopy(entinfo.origin, li->origin); + //also update the goal area number + li->goalareanum = AAS_BestReachableArea(li->origin, + ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs, + li->goalorigin); + } //end if + break; + } //end else + } //end if + } //end for + if (li) continue; + //try to link the entity to a level item + for (li = levelitems; li; li = li->next) + { + //if this level item is already linked + if (li->entitynum) continue; + // + if (g_gametype == GT_SINGLE_PLAYER) { + if (li->flags & IFL_NOTSINGLE) continue; + } + else if (g_gametype >= GT_TEAM) { + if (li->flags & IFL_NOTTEAM) continue; + } + else { + if (li->flags & IFL_NOTFREE) continue; + } + //if the model of the level item and the entity are the same + if (ic->iteminfo[li->iteminfo].modelindex == modelindex) + { + //check if the entity is very close + VectorSubtract(li->origin, entinfo.origin, dir); + if (VectorLength(dir) < 30) + { + //found an entity for this level item + li->entitynum = ent; + //if the origin is different + if (entinfo.origin[0] != li->origin[0] || + entinfo.origin[1] != li->origin[1] || + entinfo.origin[2] != li->origin[2]) + { + //update the level item origin + VectorCopy(entinfo.origin, li->origin); + //also update the goal area number + li->goalareanum = AAS_BestReachableArea(li->origin, + ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs, + li->goalorigin); + } //end if +#ifdef DEBUG + Log_Write("linked item %s to an entity", ic->iteminfo[li->iteminfo].classname); +#endif //DEBUG + break; + } //end if + } //end else + } //end for + if (li) continue; + //check if the model is from a known item + for (i = 0; i < ic->numiteminfo; i++) + { + if (ic->iteminfo[i].modelindex == modelindex) + { + break; + } //end if + } //end for + //if the model is not from a known item + if (i >= ic->numiteminfo) continue; + //allocate a new level item + li = AllocLevelItem(); + // + if (!li) continue; + //entity number of the level item + li->entitynum = ent; + //number for the level item + li->number = numlevelitems + ent; + //set the item info index for the level item + li->iteminfo = i; + //origin of the item + VectorCopy(entinfo.origin, li->origin); + //get the item goal area and goal origin + li->goalareanum = AAS_BestReachableArea(li->origin, + ic->iteminfo[i].mins, ic->iteminfo[i].maxs, + li->goalorigin); + //never go for items dropped into jumppads + if (AAS_AreaJumpPad(li->goalareanum)) + { + FreeLevelItem(li); + continue; + } //end if + //time this item out after 30 seconds + //dropped items disappear after 30 seconds + li->timeout = AAS_Time() + 30; + //add the level item to the list + AddLevelItemToList(li); + //botimport.Print(PRT_MESSAGE, "found new level item %s\n", ic->iteminfo[i].classname); + } //end for + /* + for (li = levelitems; li; li = li->next) + { + if (!li->entitynum) + { + BotFindEntityForLevelItem(li); + } //end if + } //end for*/ +} //end of the function BotUpdateEntityItems +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpGoalStack(int goalstate) +{ + int i; + bot_goalstate_t *gs; + char name[32]; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + for (i = 1; i <= gs->goalstacktop; i++) + { + BotGoalName(gs->goalstack[i].number, name, 32); + Log_Write("%d: %s", i, name); + } //end for +} //end of the function BotDumpGoalStack +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotPushGoal(int goalstate, bot_goal_t *goal) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + if (gs->goalstacktop >= MAX_GOALSTACK-1) + { + botimport.Print(PRT_ERROR, "goal heap overflow\n"); + BotDumpGoalStack(goalstate); + return; + } //end if + gs->goalstacktop++; + Com_Memcpy(&gs->goalstack[gs->goalstacktop], goal, sizeof(bot_goal_t)); +} //end of the function BotPushGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotPopGoal(int goalstate) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + if (gs->goalstacktop > 0) gs->goalstacktop--; +} //end of the function BotPopGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotEmptyGoalStack(int goalstate) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + gs->goalstacktop = 0; +} //end of the function BotEmptyGoalStack +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotGetTopGoal(int goalstate, bot_goal_t *goal) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return qfalse; + if (!gs->goalstacktop) return qfalse; + Com_Memcpy(goal, &gs->goalstack[gs->goalstacktop], sizeof(bot_goal_t)); + return qtrue; +} //end of the function BotGetTopGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotGetSecondGoal(int goalstate, bot_goal_t *goal) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return qfalse; + if (gs->goalstacktop <= 1) return qfalse; + Com_Memcpy(goal, &gs->goalstack[gs->goalstacktop-1], sizeof(bot_goal_t)); + return qtrue; +} //end of the function BotGetSecondGoal +//=========================================================================== +// pops a new long term goal on the goal stack in the goalstate +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotChooseLTGItem(int goalstate, vec3_t origin, int *inventory, int travelflags) +{ + int areanum, t, weightnum; + float weight, bestweight, avoidtime; + iteminfo_t *iteminfo; + itemconfig_t *ic; + levelitem_t *li, *bestitem; + bot_goal_t goal; + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) + return qfalse; + if (!gs->itemweightconfig) + return qfalse; + //get the area the bot is in + areanum = BotReachabilityArea(origin, gs->client); + //if the bot is in solid or if the area the bot is in has no reachability links + if (!areanum || !AAS_AreaReachability(areanum)) + { + //use the last valid area the bot was in + areanum = gs->lastreachabilityarea; + } //end if + //remember the last area with reachabilities the bot was in + gs->lastreachabilityarea = areanum; + //if still in solid + if (!areanum) + return qfalse; + //the item configuration + ic = itemconfig; + if (!itemconfig) + return qfalse; + //best weight and item so far + bestweight = 0; + bestitem = NULL; + Com_Memset(&goal, 0, sizeof(bot_goal_t)); + //go through the items in the level + for (li = levelitems; li; li = li->next) + { + if (g_gametype == GT_SINGLE_PLAYER) { + if (li->flags & IFL_NOTSINGLE) + continue; + } + else if (g_gametype >= GT_TEAM) { + if (li->flags & IFL_NOTTEAM) + continue; + } + else { + if (li->flags & IFL_NOTFREE) + continue; + } + if (li->flags & IFL_NOTBOT) + continue; + //if the item is not in a possible goal area + if (!li->goalareanum) + continue; + //FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk) + if (!li->entitynum && !(li->flags & IFL_ROAM)) + continue; + //get the fuzzy weight function for this item + iteminfo = &ic->iteminfo[li->iteminfo]; + weightnum = gs->itemweightindex[iteminfo->number]; + if (weightnum < 0) + continue; + +#ifdef UNDECIDEDFUZZY + weight = FuzzyWeightUndecided(inventory, gs->itemweightconfig, weightnum); +#else + weight = FuzzyWeight(inventory, gs->itemweightconfig, weightnum); +#endif //UNDECIDEDFUZZY +#ifdef DROPPEDWEIGHT + //HACK: to make dropped items more attractive + if (li->timeout) + weight += droppedweight->value; +#endif //DROPPEDWEIGHT + //use weight scale for item_botroam + if (li->flags & IFL_ROAM) weight *= li->weight; + // + if (weight > 0) + { + //get the travel time towards the goal area + t = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags); + //if the goal is reachable + if (t > 0) + { + //if this item won't respawn before we get there + avoidtime = BotAvoidGoalTime(goalstate, li->number); + if (avoidtime - t * 0.009 > 0) + continue; + // + weight /= (float) t * TRAVELTIME_SCALE; + // + if (weight > bestweight) + { + bestweight = weight; + bestitem = li; + } //end if + } //end if + } //end if + } //end for + //if no goal item found + if (!bestitem) + { + /* + //if not in lava or slime + if (!AAS_AreaLava(areanum) && !AAS_AreaSlime(areanum)) + { + if (AAS_RandomGoalArea(areanum, travelflags, &goal.areanum, goal.origin)) + { + VectorSet(goal.mins, -15, -15, -15); + VectorSet(goal.maxs, 15, 15, 15); + goal.entitynum = 0; + goal.number = 0; + goal.flags = GFL_ROAM; + goal.iteminfo = 0; + //push the goal on the stack + BotPushGoal(goalstate, &goal); + // +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "chosen roam goal area %d\n", goal.areanum); +#endif //DEBUG + return qtrue; + } //end if + } //end if + */ + return qfalse; + } //end if + //create a bot goal for this item + iteminfo = &ic->iteminfo[bestitem->iteminfo]; + VectorCopy(bestitem->goalorigin, goal.origin); + VectorCopy(iteminfo->mins, goal.mins); + VectorCopy(iteminfo->maxs, goal.maxs); + goal.areanum = bestitem->goalareanum; + goal.entitynum = bestitem->entitynum; + goal.number = bestitem->number; + goal.flags = GFL_ITEM; + if (bestitem->timeout) + goal.flags |= GFL_DROPPED; + if (bestitem->flags & IFL_ROAM) + goal.flags |= GFL_ROAM; + goal.iteminfo = bestitem->iteminfo; + //if it's a dropped item + if (bestitem->timeout) + { + avoidtime = AVOID_DROPPED_TIME; + } //end if + else + { + avoidtime = iteminfo->respawntime; + if (!avoidtime) + avoidtime = AVOID_DEFAULT_TIME; + if (avoidtime < AVOID_MINIMUM_TIME) + avoidtime = AVOID_MINIMUM_TIME; + } //end else + //add the chosen goal to the goals to avoid for a while + BotAddToAvoidGoals(gs, bestitem->number, avoidtime); + //push the goal on the stack + BotPushGoal(goalstate, &goal); + // + return qtrue; +} //end of the function BotChooseLTGItem +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelflags, + bot_goal_t *ltg, float maxtime) +{ + int areanum, t, weightnum, ltg_time; + float weight, bestweight, avoidtime; + iteminfo_t *iteminfo; + itemconfig_t *ic; + levelitem_t *li, *bestitem; + bot_goal_t goal; + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) + return qfalse; + if (!gs->itemweightconfig) + return qfalse; + //get the area the bot is in + areanum = BotReachabilityArea(origin, gs->client); + //if the bot is in solid or if the area the bot is in has no reachability links + if (!areanum || !AAS_AreaReachability(areanum)) + { + //use the last valid area the bot was in + areanum = gs->lastreachabilityarea; + } //end if + //remember the last area with reachabilities the bot was in + gs->lastreachabilityarea = areanum; + //if still in solid + if (!areanum) + return qfalse; + // + if (ltg) ltg_time = AAS_AreaTravelTimeToGoalArea(areanum, origin, ltg->areanum, travelflags); + else ltg_time = 99999; + //the item configuration + ic = itemconfig; + if (!itemconfig) + return qfalse; + //best weight and item so far + bestweight = 0; + bestitem = NULL; + Com_Memset(&goal, 0, sizeof(bot_goal_t)); + //go through the items in the level + for (li = levelitems; li; li = li->next) + { + if (g_gametype == GT_SINGLE_PLAYER) { + if (li->flags & IFL_NOTSINGLE) + continue; + } + else if (g_gametype >= GT_TEAM) { + if (li->flags & IFL_NOTTEAM) + continue; + } + else { + if (li->flags & IFL_NOTFREE) + continue; + } + if (li->flags & IFL_NOTBOT) + continue; + //if the item is in a possible goal area + if (!li->goalareanum) + continue; + //FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk) + if (!li->entitynum && !(li->flags & IFL_ROAM)) + continue; + //get the fuzzy weight function for this item + iteminfo = &ic->iteminfo[li->iteminfo]; + weightnum = gs->itemweightindex[iteminfo->number]; + if (weightnum < 0) + continue; + // +#ifdef UNDECIDEDFUZZY + weight = FuzzyWeightUndecided(inventory, gs->itemweightconfig, weightnum); +#else + weight = FuzzyWeight(inventory, gs->itemweightconfig, weightnum); +#endif //UNDECIDEDFUZZY +#ifdef DROPPEDWEIGHT + //HACK: to make dropped items more attractive + if (li->timeout) + weight += droppedweight->value; +#endif //DROPPEDWEIGHT + //use weight scale for item_botroam + if (li->flags & IFL_ROAM) weight *= li->weight; + // + if (weight > 0) + { + //get the travel time towards the goal area + t = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags); + //if the goal is reachable + if (t > 0 && t < maxtime) + { + //if this item won't respawn before we get there + avoidtime = BotAvoidGoalTime(goalstate, li->number); + if (avoidtime - t * 0.009 > 0) + continue; + // + weight /= (float) t * TRAVELTIME_SCALE; + // + if (weight > bestweight) + { + t = 0; + if (ltg && !li->timeout) + { + //get the travel time from the goal to the long term goal + t = AAS_AreaTravelTimeToGoalArea(li->goalareanum, li->goalorigin, ltg->areanum, travelflags); + } //end if + //if the travel back is possible and doesn't take too long + if (t <= ltg_time) + { + bestweight = weight; + bestitem = li; + } //end if + } //end if + } //end if + } //end if + } //end for + //if no goal item found + if (!bestitem) + return qfalse; + //create a bot goal for this item + iteminfo = &ic->iteminfo[bestitem->iteminfo]; + VectorCopy(bestitem->goalorigin, goal.origin); + VectorCopy(iteminfo->mins, goal.mins); + VectorCopy(iteminfo->maxs, goal.maxs); + goal.areanum = bestitem->goalareanum; + goal.entitynum = bestitem->entitynum; + goal.number = bestitem->number; + goal.flags = GFL_ITEM; + if (bestitem->timeout) + goal.flags |= GFL_DROPPED; + if (bestitem->flags & IFL_ROAM) + goal.flags |= GFL_ROAM; + goal.iteminfo = bestitem->iteminfo; + //if it's a dropped item + if (bestitem->timeout) + { + avoidtime = AVOID_DROPPED_TIME; + } //end if + else + { + avoidtime = iteminfo->respawntime; + if (!avoidtime) + avoidtime = AVOID_DEFAULT_TIME; + if (avoidtime < AVOID_MINIMUM_TIME) + avoidtime = AVOID_MINIMUM_TIME; + } //end else + //add the chosen goal to the goals to avoid for a while + BotAddToAvoidGoals(gs, bestitem->number, avoidtime); + //push the goal on the stack + BotPushGoal(goalstate, &goal); + // + return qtrue; +} //end of the function BotChooseNBGItem +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotTouchingGoal(vec3_t origin, bot_goal_t *goal) +{ + int i; + vec3_t boxmins, boxmaxs; + vec3_t absmins, absmaxs; + vec3_t safety_maxs = {0, 0, 0}; //{4, 4, 10}; + vec3_t safety_mins = {0, 0, 0}; //{-4, -4, 0}; + + AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, boxmins, boxmaxs); + VectorSubtract(goal->mins, boxmaxs, absmins); + VectorSubtract(goal->maxs, boxmins, absmaxs); + VectorAdd(absmins, goal->origin, absmins); + VectorAdd(absmaxs, goal->origin, absmaxs); + //make the box a little smaller for safety + VectorSubtract(absmaxs, safety_maxs, absmaxs); + VectorSubtract(absmins, safety_mins, absmins); + + for (i = 0; i < 3; i++) + { + if (origin[i] < absmins[i] || origin[i] > absmaxs[i]) return qfalse; + } //end for + return qtrue; +} //end of the function BotTouchingGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotItemGoalInVisButNotVisible(int viewer, vec3_t eye, vec3_t viewangles, bot_goal_t *goal) +{ + aas_entityinfo_t entinfo; + bsp_trace_t trace; + vec3_t middle; + + if (!(goal->flags & GFL_ITEM)) return qfalse; + // + VectorAdd(goal->mins, goal->mins, middle); + VectorScale(middle, 0.5, middle); + VectorAdd(goal->origin, middle, middle); + // + trace = AAS_Trace(eye, NULL, NULL, middle, viewer, CONTENTS_SOLID); + //if the goal middle point is visible + if (trace.fraction >= 1) + { + //the goal entity number doesn't have to be valid + //just assume it's valid + if (goal->entitynum <= 0) + return qfalse; + // + //if the entity data isn't valid + AAS_EntityInfo(goal->entitynum, &entinfo); + //NOTE: for some wacko reason entities are sometimes + // not updated + //if (!entinfo.valid) return qtrue; + if (entinfo.ltime < AAS_Time() - 0.5) + return qtrue; + } //end if + return qfalse; +} //end of the function BotItemGoalInVisButNotVisible +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetGoalState(int goalstate) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + Com_Memset(gs->goalstack, 0, MAX_GOALSTACK * sizeof(bot_goal_t)); + gs->goalstacktop = 0; + BotResetAvoidGoals(goalstate); +} //end of the function BotResetGoalState +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadItemWeights(int goalstate, char *filename) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return BLERR_CANNOTLOADITEMWEIGHTS; + //load the weight configuration + gs->itemweightconfig = ReadWeightConfig(filename); + if (!gs->itemweightconfig) + { + botimport.Print(PRT_FATAL, "couldn't load weights\n"); + return BLERR_CANNOTLOADITEMWEIGHTS; + } //end if + //if there's no item configuration + if (!itemconfig) return BLERR_CANNOTLOADITEMWEIGHTS; + //create the item weight index + gs->itemweightindex = ItemWeightIndex(gs->itemweightconfig, itemconfig); + //everything went ok + return BLERR_NOERROR; +} //end of the function BotLoadItemWeights +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFreeItemWeights(int goalstate) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + if (gs->itemweightconfig) FreeWeightConfig(gs->itemweightconfig); + if (gs->itemweightindex) FreeMemory(gs->itemweightindex); +} //end of the function BotFreeItemWeights +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotAllocGoalState(int client) +{ + int i; + + for (i = 1; i <= MAX_CLIENTS; i++) + { + if (!botgoalstates[i]) + { + botgoalstates[i] = GetClearedMemory(sizeof(bot_goalstate_t)); + botgoalstates[i]->client = client; + return i; + } //end if + } //end for + return 0; +} //end of the function BotAllocGoalState +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeGoalState(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "goal state handle %d out of range\n", handle); + return; + } //end if + if (!botgoalstates[handle]) + { + botimport.Print(PRT_FATAL, "invalid goal state handle %d\n", handle); + return; + } //end if + BotFreeItemWeights(handle); + FreeMemory(botgoalstates[handle]); + botgoalstates[handle] = NULL; +} //end of the function BotFreeGoalState +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotSetupGoalAI(void) +{ + char *filename; + + //check if teamplay is on + g_gametype = LibVarValue("g_gametype", "0"); + //item configuration file + filename = LibVarString("itemconfig", "items.c"); + //load the item configuration + itemconfig = LoadItemConfig(filename); + if (!itemconfig) + { + botimport.Print(PRT_FATAL, "couldn't load item config\n"); + return BLERR_CANNOTLOADITEMCONFIG; + } //end if + // + droppedweight = LibVar("droppedweight", "1000"); + //everything went ok + return BLERR_NOERROR; +} //end of the function BotSetupGoalAI +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotShutdownGoalAI(void) +{ + int i; + + if (itemconfig) FreeMemory(itemconfig); + itemconfig = NULL; + if (levelitemheap) FreeMemory(levelitemheap); + levelitemheap = NULL; + freelevelitems = NULL; + levelitems = NULL; + numlevelitems = 0; + + BotFreeInfoEntities(); + + for (i = 1; i <= MAX_CLIENTS; i++) + { + if (botgoalstates[i]) + { + BotFreeGoalState(i); + } //end if + } //end for +} //end of the function BotShutdownGoalAI diff --git a/reaction/engine/code/botlib/be_ai_goal.h b/reaction/engine/code/botlib/be_ai_goal.h new file mode 100644 index 00000000..80dad08d --- /dev/null +++ b/reaction/engine/code/botlib/be_ai_goal.h @@ -0,0 +1,118 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +/***************************************************************************** + * name: be_ai_goal.h + * + * desc: goal AI + * + * $Archive: /source/code/botlib/be_ai_goal.h $ + * + *****************************************************************************/ + +#define MAX_AVOIDGOALS 256 +#define MAX_GOALSTACK 8 + +#define GFL_NONE 0 +#define GFL_ITEM 1 +#define GFL_ROAM 2 +#define GFL_DROPPED 4 + +//a bot goal +typedef struct bot_goal_s +{ + vec3_t origin; //origin of the goal + int areanum; //area number of the goal + vec3_t mins, maxs; //mins and maxs of the goal + int entitynum; //number of the goal entity + int number; //goal number + int flags; //goal flags + int iteminfo; //item information +} bot_goal_t; + +//reset the whole goal state, but keep the item weights +void BotResetGoalState(int goalstate); +//reset avoid goals +void BotResetAvoidGoals(int goalstate); +//remove the goal with the given number from the avoid goals +void BotRemoveFromAvoidGoals(int goalstate, int number); +//push a goal onto the goal stack +void BotPushGoal(int goalstate, bot_goal_t *goal); +//pop a goal from the goal stack +void BotPopGoal(int goalstate); +//empty the bot's goal stack +void BotEmptyGoalStack(int goalstate); +//dump the avoid goals +void BotDumpAvoidGoals(int goalstate); +//dump the goal stack +void BotDumpGoalStack(int goalstate); +//get the name name of the goal with the given number +void BotGoalName(int number, char *name, int size); +//get the top goal from the stack +int BotGetTopGoal(int goalstate, bot_goal_t *goal); +//get the second goal on the stack +int BotGetSecondGoal(int goalstate, bot_goal_t *goal); +//choose the best long term goal item for the bot +int BotChooseLTGItem(int goalstate, vec3_t origin, int *inventory, int travelflags); +//choose the best nearby goal item for the bot +//the item may not be further away from the current bot position than maxtime +//also the travel time from the nearby goal towards the long term goal may not +//be larger than the travel time towards the long term goal from the current bot position +int BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelflags, + bot_goal_t *ltg, float maxtime); +//returns true if the bot touches the goal +int BotTouchingGoal(vec3_t origin, bot_goal_t *goal); +//returns true if the goal should be visible but isn't +int BotItemGoalInVisButNotVisible(int viewer, vec3_t eye, vec3_t viewangles, bot_goal_t *goal); +//search for a goal for the given classname, the index can be used +//as a start point for the search when multiple goals are available with that same classname +int BotGetLevelItemGoal(int index, char *classname, bot_goal_t *goal); +//get the next camp spot in the map +int BotGetNextCampSpotGoal(int num, bot_goal_t *goal); +//get the map location with the given name +int BotGetMapLocationGoal(char *name, bot_goal_t *goal); +//returns the avoid goal time +float BotAvoidGoalTime(int goalstate, int number); +//set the avoid goal time +void BotSetAvoidGoalTime(int goalstate, int number, float avoidtime); +//initializes the items in the level +void BotInitLevelItems(void); +//regularly update dynamic entity items (dropped weapons, flags etc.) +void BotUpdateEntityItems(void); +//interbreed the goal fuzzy logic +void BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child); +//save the goal fuzzy logic to disk +void BotSaveGoalFuzzyLogic(int goalstate, char *filename); +//mutate the goal fuzzy logic +void BotMutateGoalFuzzyLogic(int goalstate, float range); +//loads item weights for the bot +int BotLoadItemWeights(int goalstate, char *filename); +//frees the item weights of the bot +void BotFreeItemWeights(int goalstate); +//returns the handle of a newly allocated goal state +int BotAllocGoalState(int client); +//free the given goal state +void BotFreeGoalState(int handle); +//setup the goal AI +int BotSetupGoalAI(void); +//shut down the goal AI +void BotShutdownGoalAI(void); diff --git a/reaction/engine/code/botlib/be_ai_move.c b/reaction/engine/code/botlib/be_ai_move.c new file mode 100644 index 00000000..27a855a5 --- /dev/null +++ b/reaction/engine/code/botlib/be_ai_move.c @@ -0,0 +1,3570 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_move.c + * + * desc: bot movement AI + * + * $Archive: /MissionPack/code/botlib/be_ai_move.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_libvar.h" +#include "l_utils.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" + +#include "be_ea.h" +#include "be_ai_goal.h" +#include "be_ai_move.h" + + +//#define DEBUG_AI_MOVE +//#define DEBUG_ELEVATOR +//#define DEBUG_GRAPPLE + +//movement state +//NOTE: the moveflags MFL_ONGROUND, MFL_TELEPORTED, MFL_WATERJUMP and +// MFL_GRAPPLEPULL must be set outside the movement code +typedef struct bot_movestate_s +{ + //input vars (all set outside the movement code) + vec3_t origin; //origin of the bot + vec3_t velocity; //velocity of the bot + vec3_t viewoffset; //view offset + int entitynum; //entity number of the bot + int client; //client number of the bot + float thinktime; //time the bot thinks + int presencetype; //presencetype of the bot + vec3_t viewangles; //view angles of the bot + //state vars + int areanum; //area the bot is in + int lastareanum; //last area the bot was in + int lastgoalareanum; //last goal area number + int lastreachnum; //last reachability number + vec3_t lastorigin; //origin previous cycle + int reachareanum; //area number of the reachabilty + int moveflags; //movement flags + int jumpreach; //set when jumped + float grapplevisible_time; //last time the grapple was visible + float lastgrappledist; //last distance to the grapple end + float reachability_time; //time to use current reachability + int avoidreach[MAX_AVOIDREACH]; //reachabilities to avoid + float avoidreachtimes[MAX_AVOIDREACH]; //times to avoid the reachabilities + int avoidreachtries[MAX_AVOIDREACH]; //number of tries before avoiding + // + bot_avoidspot_t avoidspots[MAX_AVOIDSPOTS]; //spots to avoid + int numavoidspots; +} bot_movestate_t; + +//used to avoid reachability links for some time after being used +#define AVOIDREACH +#define AVOIDREACH_TIME 6 //avoid links for 6 seconds after use +#define AVOIDREACH_TRIES 4 +//prediction times +#define PREDICTIONTIME_JUMP 3 //in seconds +#define PREDICTIONTIME_MOVE 2 //in seconds +//weapon indexes for weapon jumping +#define WEAPONINDEX_ROCKET_LAUNCHER 5 +#define WEAPONINDEX_BFG 9 + +#define MODELTYPE_FUNC_PLAT 1 +#define MODELTYPE_FUNC_BOB 2 +#define MODELTYPE_FUNC_DOOR 3 +#define MODELTYPE_FUNC_STATIC 4 + +libvar_t *sv_maxstep; +libvar_t *sv_maxbarrier; +libvar_t *sv_gravity; +libvar_t *weapindex_rocketlauncher; +libvar_t *weapindex_bfg10k; +libvar_t *weapindex_grapple; +libvar_t *entitytypemissile; +libvar_t *offhandgrapple; +libvar_t *cmd_grappleoff; +libvar_t *cmd_grappleon; +//type of model, func_plat or func_bobbing +int modeltypes[MAX_MODELS]; + +bot_movestate_t *botmovestates[MAX_CLIENTS+1]; + +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +int BotAllocMoveState(void) +{ + int i; + + for (i = 1; i <= MAX_CLIENTS; i++) + { + if (!botmovestates[i]) + { + botmovestates[i] = GetClearedMemory(sizeof(bot_movestate_t)); + return i; + } //end if + } //end for + return 0; +} //end of the function BotAllocMoveState +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeMoveState(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); + return; + } //end if + if (!botmovestates[handle]) + { + botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); + return; + } //end if + FreeMemory(botmovestates[handle]); + botmovestates[handle] = NULL; +} //end of the function BotFreeMoveState +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +bot_movestate_t *BotMoveStateFromHandle(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); + return NULL; + } //end if + if (!botmovestates[handle]) + { + botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); + return NULL; + } //end if + return botmovestates[handle]; +} //end of the function BotMoveStateFromHandle +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotInitMoveState(int handle, bot_initmove_t *initmove) +{ + bot_movestate_t *ms; + + ms = BotMoveStateFromHandle(handle); + if (!ms) return; + VectorCopy(initmove->origin, ms->origin); + VectorCopy(initmove->velocity, ms->velocity); + VectorCopy(initmove->viewoffset, ms->viewoffset); + ms->entitynum = initmove->entitynum; + ms->client = initmove->client; + ms->thinktime = initmove->thinktime; + ms->presencetype = initmove->presencetype; + VectorCopy(initmove->viewangles, ms->viewangles); + // + ms->moveflags &= ~MFL_ONGROUND; + if (initmove->or_moveflags & MFL_ONGROUND) ms->moveflags |= MFL_ONGROUND; + ms->moveflags &= ~MFL_TELEPORTED; + if (initmove->or_moveflags & MFL_TELEPORTED) ms->moveflags |= MFL_TELEPORTED; + ms->moveflags &= ~MFL_WATERJUMP; + if (initmove->or_moveflags & MFL_WATERJUMP) ms->moveflags |= MFL_WATERJUMP; + ms->moveflags &= ~MFL_WALK; + if (initmove->or_moveflags & MFL_WALK) ms->moveflags |= MFL_WALK; + ms->moveflags &= ~MFL_GRAPPLEPULL; + if (initmove->or_moveflags & MFL_GRAPPLEPULL) ms->moveflags |= MFL_GRAPPLEPULL; +} //end of the function BotInitMoveState +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +float AngleDiff(float ang1, float ang2) +{ + float diff; + + diff = ang1 - ang2; + if (ang1 > ang2) + { + if (diff > 180.0) diff -= 360.0; + } //end if + else + { + if (diff < -180.0) diff += 360.0; + } //end else + return diff; +} //end of the function AngleDiff +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotFuzzyPointReachabilityArea(vec3_t origin) +{ + int firstareanum, j, x, y, z; + int areas[10], numareas, areanum, bestareanum; + float dist, bestdist; + vec3_t points[10], v, end; + + firstareanum = 0; + areanum = AAS_PointAreaNum(origin); + if (areanum) + { + firstareanum = areanum; + if (AAS_AreaReachability(areanum)) return areanum; + } //end if + VectorCopy(origin, end); + end[2] += 4; + numareas = AAS_TraceAreas(origin, end, areas, points, 10); + for (j = 0; j < numareas; j++) + { + if (AAS_AreaReachability(areas[j])) return areas[j]; + } //end for + bestdist = 999999; + bestareanum = 0; + for (z = 1; z >= -1; z -= 1) + { + for (x = 1; x >= -1; x -= 1) + { + for (y = 1; y >= -1; y -= 1) + { + VectorCopy(origin, end); + end[0] += x * 8; + end[1] += y * 8; + end[2] += z * 12; + numareas = AAS_TraceAreas(origin, end, areas, points, 10); + for (j = 0; j < numareas; j++) + { + if (AAS_AreaReachability(areas[j])) + { + VectorSubtract(points[j], origin, v); + dist = VectorLength(v); + if (dist < bestdist) + { + bestareanum = areas[j]; + bestdist = dist; + } //end if + } //end if + if (!firstareanum) firstareanum = areas[j]; + } //end for + } //end for + } //end for + if (bestareanum) return bestareanum; + } //end for + return firstareanum; +} //end of the function BotFuzzyPointReachabilityArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotReachabilityArea(vec3_t origin, int client) +{ + int modelnum, modeltype, reachnum, areanum; + aas_reachability_t reach; + vec3_t org, end, mins, maxs, up = {0, 0, 1}; + bsp_trace_t bsptrace; + aas_trace_t trace; + + //check if the bot is standing on something + AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, mins, maxs); + VectorMA(origin, -3, up, end); + bsptrace = AAS_Trace(origin, mins, maxs, end, client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); + if (!bsptrace.startsolid && bsptrace.fraction < 1 && bsptrace.ent != ENTITYNUM_NONE) + { + //if standing on the world the bot should be in a valid area + if (bsptrace.ent == ENTITYNUM_WORLD) + { + return BotFuzzyPointReachabilityArea(origin); + } //end if + + modelnum = AAS_EntityModelindex(bsptrace.ent); + modeltype = modeltypes[modelnum]; + + //if standing on a func_plat or func_bobbing then the bot is assumed to be + //in the area the reachability points to + if (modeltype == MODELTYPE_FUNC_PLAT || modeltype == MODELTYPE_FUNC_BOB) + { + reachnum = AAS_NextModelReachability(0, modelnum); + if (reachnum) + { + AAS_ReachabilityFromNum(reachnum, &reach); + return reach.areanum; + } //end if + } //end else if + + //if the bot is swimming the bot should be in a valid area + if (AAS_Swimming(origin)) + { + return BotFuzzyPointReachabilityArea(origin); + } //end if + // + areanum = BotFuzzyPointReachabilityArea(origin); + //if the bot is in an area with reachabilities + if (areanum && AAS_AreaReachability(areanum)) return areanum; + //trace down till the ground is hit because the bot is standing on some other entity + VectorCopy(origin, org); + VectorCopy(org, end); + end[2] -= 800; + trace = AAS_TraceClientBBox(org, end, PRESENCE_CROUCH, -1); + if (!trace.startsolid) + { + VectorCopy(trace.endpos, org); + } //end if + // + return BotFuzzyPointReachabilityArea(org); + } //end if + // + return BotFuzzyPointReachabilityArea(origin); +} //end of the function BotReachabilityArea +//=========================================================================== +// returns the reachability area the bot is in +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +int BotReachabilityArea(vec3_t origin, int testground) +{ + int firstareanum, i, j, x, y, z; + int areas[10], numareas, areanum, bestareanum; + float dist, bestdist; + vec3_t org, end, points[10], v; + aas_trace_t trace; + + firstareanum = 0; + for (i = 0; i < 2; i++) + { + VectorCopy(origin, org); + //if test at the ground (used when bot is standing on an entity) + if (i > 0) + { + VectorCopy(origin, end); + end[2] -= 800; + trace = AAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1); + if (!trace.startsolid) + { + VectorCopy(trace.endpos, org); + } //end if + } //end if + + firstareanum = 0; + areanum = AAS_PointAreaNum(org); + if (areanum) + { + firstareanum = areanum; + if (AAS_AreaReachability(areanum)) return areanum; + } //end if + bestdist = 999999; + bestareanum = 0; + for (z = 1; z >= -1; z -= 1) + { + for (x = 1; x >= -1; x -= 1) + { + for (y = 1; y >= -1; y -= 1) + { + VectorCopy(org, end); + end[0] += x * 8; + end[1] += y * 8; + end[2] += z * 12; + numareas = AAS_TraceAreas(org, end, areas, points, 10); + for (j = 0; j < numareas; j++) + { + if (AAS_AreaReachability(areas[j])) + { + VectorSubtract(points[j], org, v); + dist = VectorLength(v); + if (dist < bestdist) + { + bestareanum = areas[j]; + bestdist = dist; + } //end if + } //end if + } //end for + } //end for + } //end for + if (bestareanum) return bestareanum; + } //end for + if (!testground) break; + } //end for +//#ifdef DEBUG + //botimport.Print(PRT_MESSAGE, "no reachability area\n"); +//#endif //DEBUG + return firstareanum; +} //end of the function BotReachabilityArea*/ +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotOnMover(vec3_t origin, int entnum, aas_reachability_t *reach) +{ + int i, modelnum; + vec3_t mins, maxs, modelorigin, org, end; + vec3_t angles = {0, 0, 0}; + vec3_t boxmins = {-16, -16, -8}, boxmaxs = {16, 16, 8}; + bsp_trace_t trace; + + modelnum = reach->facenum & 0x0000FFFF; + //get some bsp model info + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL); + // + if (!AAS_OriginOfMoverWithModelNum(modelnum, modelorigin)) + { + botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum); + return qfalse; + } //end if + // + for (i = 0; i < 2; i++) + { + if (origin[i] > modelorigin[i] + maxs[i] + 16) return qfalse; + if (origin[i] < modelorigin[i] + mins[i] - 16) return qfalse; + } //end for + // + VectorCopy(origin, org); + org[2] += 24; + VectorCopy(origin, end); + end[2] -= 48; + // + trace = AAS_Trace(org, boxmins, boxmaxs, end, entnum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); + if (!trace.startsolid && !trace.allsolid) + { + //NOTE: the reachability face number is the model number of the elevator + if (trace.ent != ENTITYNUM_NONE && AAS_EntityModelNum(trace.ent) == modelnum) + { + return qtrue; + } //end if + } //end if + return qfalse; +} //end of the function BotOnMover +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int MoverDown(aas_reachability_t *reach) +{ + int modelnum; + vec3_t mins, maxs, origin; + vec3_t angles = {0, 0, 0}; + + modelnum = reach->facenum & 0x0000FFFF; + //get some bsp model info + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); + // + if (!AAS_OriginOfMoverWithModelNum(modelnum, origin)) + { + botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum); + return qfalse; + } //end if + //if the top of the plat is below the reachability start point + if (origin[2] + maxs[2] < reach->start[2]) return qtrue; + return qfalse; +} //end of the function MoverDown +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotSetBrushModelTypes(void) +{ + int ent, modelnum; + char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; + + Com_Memset(modeltypes, 0, MAX_MODELS * sizeof(int)); + // + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) continue; + if (model[0]) modelnum = atoi(model+1); + else modelnum = 0; + + if (modelnum < 0 || modelnum > MAX_MODELS) + { + botimport.Print(PRT_MESSAGE, "entity %s model number out of range\n", classname); + continue; + } //end if + + if (!Q_stricmp(classname, "func_bobbing")) + modeltypes[modelnum] = MODELTYPE_FUNC_BOB; + else if (!Q_stricmp(classname, "func_plat")) + modeltypes[modelnum] = MODELTYPE_FUNC_PLAT; + else if (!Q_stricmp(classname, "func_door")) + modeltypes[modelnum] = MODELTYPE_FUNC_DOOR; + else if (!Q_stricmp(classname, "func_static")) + modeltypes[modelnum] = MODELTYPE_FUNC_STATIC; + } //end for +} //end of the function BotSetBrushModelTypes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotOnTopOfEntity(bot_movestate_t *ms) +{ + vec3_t mins, maxs, end, up = {0, 0, 1}; + bsp_trace_t trace; + + AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs); + VectorMA(ms->origin, -3, up, end); + trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); + if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) ) + { + return trace.ent; + } //end if + return -1; +} //end of the function BotOnTopOfEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotValidTravel(vec3_t origin, aas_reachability_t *reach, int travelflags) +{ + //if the reachability uses an unwanted travel type + if (AAS_TravelFlagForType(reach->traveltype) & ~travelflags) return qfalse; + //don't go into areas with bad travel types + if (AAS_AreaContentsTravelFlags(reach->areanum) & ~travelflags) return qfalse; + return qtrue; +} //end of the function BotValidTravel +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotAddToAvoidReach(bot_movestate_t *ms, int number, float avoidtime) +{ + int i; + + for (i = 0; i < MAX_AVOIDREACH; i++) + { + if (ms->avoidreach[i] == number) + { + if (ms->avoidreachtimes[i] > AAS_Time()) ms->avoidreachtries[i]++; + else ms->avoidreachtries[i] = 1; + ms->avoidreachtimes[i] = AAS_Time() + avoidtime; + return; + } //end if + } //end for + //add the reachability to the reachabilities to avoid for a while + for (i = 0; i < MAX_AVOIDREACH; i++) + { + if (ms->avoidreachtimes[i] < AAS_Time()) + { + ms->avoidreach[i] = number; + ms->avoidreachtimes[i] = AAS_Time() + avoidtime; + ms->avoidreachtries[i] = 1; + return; + } //end if + } //end for +} //end of the function BotAddToAvoidReach +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float DistanceFromLineSquared(vec3_t p, vec3_t lp1, vec3_t lp2) +{ + vec3_t proj, dir; + int j; + + AAS_ProjectPointOntoVector(p, lp1, lp2, proj); + for (j = 0; j < 3; j++) + if ((proj[j] > lp1[j] && proj[j] > lp2[j]) || + (proj[j] < lp1[j] && proj[j] < lp2[j])) + break; + if (j < 3) { + if (fabs(proj[j] - lp1[j]) < fabs(proj[j] - lp2[j])) + VectorSubtract(p, lp1, dir); + else + VectorSubtract(p, lp2, dir); + return VectorLengthSquared(dir); + } + VectorSubtract(p, proj, dir); + return VectorLengthSquared(dir); +} //end of the function DistanceFromLineSquared +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float VectorDistanceSquared(vec3_t p1, vec3_t p2) +{ + vec3_t dir; + VectorSubtract(p2, p1, dir); + return VectorLengthSquared(dir); +} //end of the function VectorDistanceSquared +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotAvoidSpots(vec3_t origin, aas_reachability_t *reach, bot_avoidspot_t *avoidspots, int numavoidspots) +{ + int checkbetween, i, type; + float squareddist, squaredradius; + + switch(reach->traveltype & TRAVELTYPE_MASK) + { + case TRAVEL_WALK: checkbetween = qtrue; break; + case TRAVEL_CROUCH: checkbetween = qtrue; break; + case TRAVEL_BARRIERJUMP: checkbetween = qtrue; break; + case TRAVEL_LADDER: checkbetween = qtrue; break; + case TRAVEL_WALKOFFLEDGE: checkbetween = qfalse; break; + case TRAVEL_JUMP: checkbetween = qfalse; break; + case TRAVEL_SWIM: checkbetween = qtrue; break; + case TRAVEL_WATERJUMP: checkbetween = qtrue; break; + case TRAVEL_TELEPORT: checkbetween = qfalse; break; + case TRAVEL_ELEVATOR: checkbetween = qfalse; break; + case TRAVEL_GRAPPLEHOOK: checkbetween = qfalse; break; + case TRAVEL_ROCKETJUMP: checkbetween = qfalse; break; + case TRAVEL_BFGJUMP: checkbetween = qfalse; break; + case TRAVEL_JUMPPAD: checkbetween = qfalse; break; + case TRAVEL_FUNCBOB: checkbetween = qfalse; break; + default: checkbetween = qtrue; break; + } //end switch + + type = AVOID_CLEAR; + for (i = 0; i < numavoidspots; i++) + { + squaredradius = Square(avoidspots[i].radius); + squareddist = DistanceFromLineSquared(avoidspots[i].origin, origin, reach->start); + // if moving towards the avoid spot + if (squareddist < squaredradius && + VectorDistanceSquared(avoidspots[i].origin, origin) > squareddist) + { + type = avoidspots[i].type; + } //end if + else if (checkbetween) { + squareddist = DistanceFromLineSquared(avoidspots[i].origin, reach->start, reach->end); + // if moving towards the avoid spot + if (squareddist < squaredradius && + VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist) + { + type = avoidspots[i].type; + } //end if + } //end if + else + { + VectorDistanceSquared(avoidspots[i].origin, reach->end); + // if the reachability leads closer to the avoid spot + if (squareddist < squaredradius && + VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist) + { + type = avoidspots[i].type; + } //end if + } //end else + if (type == AVOID_ALWAYS) + return type; + } //end for + return type; +} //end of the function BotAvoidSpots +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type) +{ + bot_movestate_t *ms; + + ms = BotMoveStateFromHandle(movestate); + if (!ms) return; + if (type == AVOID_CLEAR) + { + ms->numavoidspots = 0; + return; + } //end if + + if (ms->numavoidspots >= MAX_AVOIDSPOTS) + return; + VectorCopy(origin, ms->avoidspots[ms->numavoidspots].origin); + ms->avoidspots[ms->numavoidspots].radius = radius; + ms->avoidspots[ms->numavoidspots].type = type; + ms->numavoidspots++; +} //end of the function BotAddAvoidSpot +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotGetReachabilityToGoal(vec3_t origin, int areanum, + int lastgoalareanum, int lastareanum, + int *avoidreach, float *avoidreachtimes, int *avoidreachtries, + bot_goal_t *goal, int travelflags, int movetravelflags, + struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags) +{ + int i, t, besttime, bestreachnum, reachnum; + aas_reachability_t reach; + + //if not in a valid area + if (!areanum) return 0; + // + if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goal->areanum)) + { + travelflags |= TFL_DONOTENTER; + movetravelflags |= TFL_DONOTENTER; + } //end if + //use the routing to find the next area to go to + besttime = 0; + bestreachnum = 0; + // + for (reachnum = AAS_NextAreaReachability(areanum, 0); reachnum; + reachnum = AAS_NextAreaReachability(areanum, reachnum)) + { +#ifdef AVOIDREACH + //check if it isn't an reachability to avoid + for (i = 0; i < MAX_AVOIDREACH; i++) + { + if (avoidreach[i] == reachnum && avoidreachtimes[i] >= AAS_Time()) break; + } //end for + if (i != MAX_AVOIDREACH && avoidreachtries[i] > AVOIDREACH_TRIES) + { +#ifdef DEBUG + if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "avoiding reachability %d\n", avoidreach[i]); + } //end if +#endif //DEBUG + continue; + } //end if +#endif //AVOIDREACH + //get the reachability from the number + AAS_ReachabilityFromNum(reachnum, &reach); + //NOTE: do not go back to the previous area if the goal didn't change + //NOTE: is this actually avoidance of local routing minima between two areas??? + if (lastgoalareanum == goal->areanum && reach.areanum == lastareanum) continue; + //if (AAS_AreaContentsTravelFlags(reach.areanum) & ~travelflags) continue; + //if the travel isn't valid + if (!BotValidTravel(origin, &reach, movetravelflags)) continue; + //get the travel time + t = AAS_AreaTravelTimeToGoalArea(reach.areanum, reach.end, goal->areanum, travelflags); + //if the goal area isn't reachable from the reachable area + if (!t) continue; + //if the bot should not use this reachability to avoid bad spots + if (BotAvoidSpots(origin, &reach, avoidspots, numavoidspots)) { + if (flags) { + *flags |= MOVERESULT_BLOCKEDBYAVOIDSPOT; + } + continue; + } + //add the travel time towards the area + t += reach.traveltime;// + AAS_AreaTravelTime(areanum, origin, reach.start); + //if the travel time is better than the ones already found + if (!besttime || t < besttime) + { + besttime = t; + bestreachnum = reachnum; + } //end if + } //end for + // + return bestreachnum; +} //end of the function BotGetReachabilityToGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotAddToTarget(vec3_t start, vec3_t end, float maxdist, float *dist, vec3_t target) +{ + vec3_t dir; + float curdist; + + VectorSubtract(end, start, dir); + curdist = VectorNormalize(dir); + if (*dist + curdist < maxdist) + { + VectorCopy(end, target); + *dist += curdist; + return qfalse; + } //end if + else + { + VectorMA(start, maxdist - *dist, dir, target); + *dist = maxdist; + return qtrue; + } //end else +} //end of the function BotAddToTarget + +int BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target) +{ + aas_reachability_t reach; + int reachnum, lastareanum; + bot_movestate_t *ms; + vec3_t end; + float dist; + + ms = BotMoveStateFromHandle(movestate); + if (!ms) return qfalse; + reachnum = 0; + //if the bot has no goal or no last reachability + if (!ms->lastreachnum || !goal) return qfalse; + + reachnum = ms->lastreachnum; + VectorCopy(ms->origin, end); + lastareanum = ms->lastareanum; + dist = 0; + while(reachnum && dist < lookahead) + { + AAS_ReachabilityFromNum(reachnum, &reach); + if (BotAddToTarget(end, reach.start, lookahead, &dist, target)) return qtrue; + //never look beyond teleporters + if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_TELEPORT) return qtrue; + //never look beyond the weapon jump point + if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) return qtrue; + if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_BFGJUMP) return qtrue; + //don't add jump pad distances + if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_JUMPPAD && + (reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR && + (reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB) + { + if (BotAddToTarget(reach.start, reach.end, lookahead, &dist, target)) return qtrue; + } //end if + reachnum = BotGetReachabilityToGoal(reach.end, reach.areanum, + ms->lastgoalareanum, lastareanum, + ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries, + goal, travelflags, travelflags, NULL, 0, NULL); + VectorCopy(reach.end, end); + lastareanum = reach.areanum; + if (lastareanum == goal->areanum) + { + BotAddToTarget(reach.end, goal->origin, lookahead, &dist, target); + return qtrue; + } //end if + } //end while + // + return qfalse; +} //end of the function BotMovementViewTarget +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotVisible(int ent, vec3_t eye, vec3_t target) +{ + bsp_trace_t trace; + + trace = AAS_Trace(eye, NULL, NULL, target, ent, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); + if (trace.fraction >= 1) return qtrue; + return qfalse; +} //end of the function BotVisible +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target) +{ + aas_reachability_t reach; + int reachnum, lastgoalareanum, lastareanum, i; + int avoidreach[MAX_AVOIDREACH]; + float avoidreachtimes[MAX_AVOIDREACH]; + int avoidreachtries[MAX_AVOIDREACH]; + vec3_t end; + + //if the bot has no goal or no last reachability + if (!goal) return qfalse; + //if the areanum is not valid + if (!areanum) return qfalse; + //if the goal areanum is not valid + if (!goal->areanum) return qfalse; + + Com_Memset(avoidreach, 0, MAX_AVOIDREACH * sizeof(int)); + lastgoalareanum = goal->areanum; + lastareanum = areanum; + VectorCopy(origin, end); + //only do 20 hops + for (i = 0; i < 20 && (areanum != goal->areanum); i++) + { + // + reachnum = BotGetReachabilityToGoal(end, areanum, + lastgoalareanum, lastareanum, + avoidreach, avoidreachtimes, avoidreachtries, + goal, travelflags, travelflags, NULL, 0, NULL); + if (!reachnum) return qfalse; + AAS_ReachabilityFromNum(reachnum, &reach); + // + if (BotVisible(goal->entitynum, goal->origin, reach.start)) + { + VectorCopy(reach.start, target); + return qtrue; + } //end if + // + if (BotVisible(goal->entitynum, goal->origin, reach.end)) + { + VectorCopy(reach.end, target); + return qtrue; + } //end if + // + if (reach.areanum == goal->areanum) + { + VectorCopy(reach.end, target); + return qtrue; + } //end if + // + lastareanum = areanum; + areanum = reach.areanum; + VectorCopy(reach.end, end); + // + } //end while + // + return qfalse; +} //end of the function BotPredictVisiblePosition +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void MoverBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter) +{ + int modelnum; + vec3_t mins, maxs, origin, mids; + vec3_t angles = {0, 0, 0}; + + modelnum = reach->facenum & 0x0000FFFF; + //get some bsp model info + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); + // + if (!AAS_OriginOfMoverWithModelNum(modelnum, origin)) + { + botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum); + } //end if + //get a point just above the plat in the bottom position + VectorAdd(mins, maxs, mids); + VectorMA(origin, 0.5, mids, bottomcenter); + bottomcenter[2] = reach->start[2]; +} //end of the function MoverBottomCenter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float BotGapDistance(vec3_t origin, vec3_t hordir, int entnum) +{ + float dist, startz; + vec3_t start, end; + aas_trace_t trace; + + //do gap checking + startz = origin[2]; + //this enables walking down stairs more fluidly + { + VectorCopy(origin, start); + VectorCopy(origin, end); + end[2] -= 60; + trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum); + if (trace.fraction >= 1) return 1; + startz = trace.endpos[2] + 1; + } + // + for (dist = 8; dist <= 100; dist += 8) + { + VectorMA(origin, dist, hordir, start); + start[2] = startz + 24; + VectorCopy(start, end); + end[2] -= 48 + sv_maxbarrier->value; + trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum); + //if solid is found the bot can't walk any further and fall into a gap + if (!trace.startsolid) + { + //if it is a gap + if (trace.endpos[2] < startz - sv_maxstep->value - 8) + { + VectorCopy(trace.endpos, end); + end[2] -= 20; + if (AAS_PointContents(end) & CONTENTS_WATER) break; + //if a gap is found slow down + //botimport.Print(PRT_MESSAGE, "gap at %f\n", dist); + return dist; + } //end if + startz = trace.endpos[2]; + } //end if + } //end for + return 0; +} //end of the function BotGapDistance +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotCheckBarrierJump(bot_movestate_t *ms, vec3_t dir, float speed) +{ + vec3_t start, hordir, end; + aas_trace_t trace; + + VectorCopy(ms->origin, end); + end[2] += sv_maxbarrier->value; + //trace right up + trace = AAS_TraceClientBBox(ms->origin, end, PRESENCE_NORMAL, ms->entitynum); + //this shouldn't happen... but we check anyway + if (trace.startsolid) return qfalse; + //if very low ceiling it isn't possible to jump up to a barrier + if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse; + // + hordir[0] = dir[0]; + hordir[1] = dir[1]; + hordir[2] = 0; + VectorNormalize(hordir); + VectorMA(ms->origin, ms->thinktime * speed * 0.5, hordir, end); + VectorCopy(trace.endpos, start); + end[2] = trace.endpos[2]; + //trace from previous trace end pos horizontally in the move direction + trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum); + //again this shouldn't happen + if (trace.startsolid) return qfalse; + // + VectorCopy(trace.endpos, start); + VectorCopy(trace.endpos, end); + end[2] = ms->origin[2]; + //trace down from the previous trace end pos + trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum); + //if solid + if (trace.startsolid) return qfalse; + //if no obstacle at all + if (trace.fraction >= 1.0) return qfalse; + //if less than the maximum step height + if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse; + // + EA_Jump(ms->client); + EA_Move(ms->client, hordir, speed); + ms->moveflags |= MFL_BARRIERJUMP; + //there is a barrier + return qtrue; +} //end of the function BotCheckBarrierJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotSwimInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type) +{ + vec3_t normdir; + + VectorCopy(dir, normdir); + VectorNormalize(normdir); + EA_Move(ms->client, normdir, speed); + return qtrue; +} //end of the function BotSwimInDirection +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotWalkInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type) +{ + vec3_t hordir, cmdmove, velocity, tmpdir, origin; + int presencetype, maxframes, cmdframes, stopevent; + aas_clientmove_t move; + float dist; + + if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND; + //if the bot is on the ground + if (ms->moveflags & MFL_ONGROUND) + { + //if there is a barrier the bot can jump on + if (BotCheckBarrierJump(ms, dir, speed)) return qtrue; + //remove barrier jump flag + ms->moveflags &= ~MFL_BARRIERJUMP; + //get the presence type for the movement + if ((type & MOVE_CROUCH) && !(type & MOVE_JUMP)) presencetype = PRESENCE_CROUCH; + else presencetype = PRESENCE_NORMAL; + //horizontal direction + hordir[0] = dir[0]; + hordir[1] = dir[1]; + hordir[2] = 0; + VectorNormalize(hordir); + //if the bot is not supposed to jump + if (!(type & MOVE_JUMP)) + { + //if there is a gap, try to jump over it + if (BotGapDistance(ms->origin, hordir, ms->entitynum) > 0) type |= MOVE_JUMP; + } //end if + //get command movement + VectorScale(hordir, speed, cmdmove); + VectorCopy(ms->velocity, velocity); + // + if (type & MOVE_JUMP) + { + //botimport.Print(PRT_MESSAGE, "trying jump\n"); + cmdmove[2] = 400; + maxframes = PREDICTIONTIME_JUMP / 0.1; + cmdframes = 1; + stopevent = SE_HITGROUND|SE_HITGROUNDDAMAGE| + SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA; + } //end if + else + { + maxframes = 2; + cmdframes = 2; + stopevent = SE_HITGROUNDDAMAGE| + SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA; + } //end else + //AAS_ClearShownDebugLines(); + // + VectorCopy(ms->origin, origin); + origin[2] += 0.5; + AAS_PredictClientMovement(&move, ms->entitynum, origin, presencetype, qtrue, + velocity, cmdmove, cmdframes, maxframes, 0.1f, + stopevent, 0, qfalse);//qtrue); + //if prediction time wasn't enough to fully predict the movement + if (move.frames >= maxframes && (type & MOVE_JUMP)) + { + //botimport.Print(PRT_MESSAGE, "client %d: max prediction frames\n", ms->client); + return qfalse; + } //end if + //don't enter slime or lava and don't fall from too high + if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) + { + //botimport.Print(PRT_MESSAGE, "client %d: would be hurt ", ms->client); + //if (move.stopevent & SE_ENTERSLIME) botimport.Print(PRT_MESSAGE, "slime\n"); + //if (move.stopevent & SE_ENTERLAVA) botimport.Print(PRT_MESSAGE, "lava\n"); + //if (move.stopevent & SE_HITGROUNDDAMAGE) botimport.Print(PRT_MESSAGE, "hitground\n"); + return qfalse; + } //end if + //if ground was hit + if (move.stopevent & SE_HITGROUND) + { + //check for nearby gap + VectorNormalize2(move.velocity, tmpdir); + dist = BotGapDistance(move.endpos, tmpdir, ms->entitynum); + if (dist > 0) return qfalse; + // + dist = BotGapDistance(move.endpos, hordir, ms->entitynum); + if (dist > 0) return qfalse; + } //end if + //get horizontal movement + tmpdir[0] = move.endpos[0] - ms->origin[0]; + tmpdir[1] = move.endpos[1] - ms->origin[1]; + tmpdir[2] = 0; + // + //AAS_DrawCross(move.endpos, 4, LINECOLOR_BLUE); + //the bot is blocked by something + if (VectorLength(tmpdir) < speed * ms->thinktime * 0.5) return qfalse; + //perform the movement + if (type & MOVE_JUMP) EA_Jump(ms->client); + if (type & MOVE_CROUCH) EA_Crouch(ms->client); + EA_Move(ms->client, hordir, speed); + //movement was succesfull + return qtrue; + } //end if + else + { + if (ms->moveflags & MFL_BARRIERJUMP) + { + //if near the top or going down + if (ms->velocity[2] < 50) + { + EA_Move(ms->client, dir, speed); + } //end if + } //end if + //FIXME: do air control to avoid hazards + return qtrue; + } //end else +} //end of the function BotWalkInDirection +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotMoveInDirection(int movestate, vec3_t dir, float speed, int type) +{ + bot_movestate_t *ms; + + ms = BotMoveStateFromHandle(movestate); + if (!ms) return qfalse; + //if swimming + if (AAS_Swimming(ms->origin)) + { + return BotSwimInDirection(ms, dir, speed, type); + } //end if + else + { + return BotWalkInDirection(ms, dir, speed, type); + } //end else +} //end of the function BotMoveInDirection +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Intersection(vec2_t p1, vec2_t p2, vec2_t p3, vec2_t p4, vec2_t out) +{ + float x1, dx1, dy1, x2, dx2, dy2, d; + + dx1 = p2[0] - p1[0]; + dy1 = p2[1] - p1[1]; + dx2 = p4[0] - p3[0]; + dy2 = p4[1] - p3[1]; + + d = dy1 * dx2 - dx1 * dy2; + if (d != 0) + { + x1 = p1[1] * dx1 - p1[0] * dy1; + x2 = p3[1] * dx2 - p3[0] * dy2; + out[0] = (int) ((dx1 * x2 - dx2 * x1) / d); + out[1] = (int) ((dy1 * x2 - dy2 * x1) / d); + return qtrue; + } //end if + else + { + return qfalse; + } //end else +} //end of the function Intersection +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotCheckBlocked(bot_movestate_t *ms, vec3_t dir, int checkbottom, bot_moveresult_t *result) +{ + vec3_t mins, maxs, end, up = {0, 0, 1}; + bsp_trace_t trace; + + //test for entities obstructing the bot's path + AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs); + // + if (fabs(DotProduct(dir, up)) < 0.7) + { + mins[2] += sv_maxstep->value; //if the bot can step on + maxs[2] -= 10; //a little lower to avoid low ceiling + } //end if + VectorMA(ms->origin, 3, dir, end); + trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY); + //if not started in solid and not hitting the world entity + if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) ) + { + result->blocked = qtrue; + result->blockentity = trace.ent; +#ifdef DEBUG + //botimport.Print(PRT_MESSAGE, "%d: BotCheckBlocked: I'm blocked\n", ms->client); +#endif //DEBUG + } //end if + //if not in an area with reachability + else if (checkbottom && !AAS_AreaReachability(ms->areanum)) + { + //check if the bot is standing on something + AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs); + VectorMA(ms->origin, -3, up, end); + trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); + if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) ) + { + result->blocked = qtrue; + result->blockentity = trace.ent; + result->flags |= MOVERESULT_ONTOPOFOBSTACLE; +#ifdef DEBUG + //botimport.Print(PRT_MESSAGE, "%d: BotCheckBlocked: I'm blocked\n", ms->client); +#endif //DEBUG + } //end if + } //end else +} //end of the function BotCheckBlocked +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach) +{ + float dist, speed; + vec3_t hordir; + bot_moveresult_t_cleared( result ); + + //first walk straight to the reachability start + hordir[0] = reach->start[0] - ms->origin[0]; + hordir[1] = reach->start[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + BotCheckBlocked(ms, hordir, qtrue, &result); + // + if (dist < 10) + { + //walk straight to the reachability end + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + } //end if + //if going towards a crouch area + if (!(AAS_AreaPresenceType(reach->areanum) & PRESENCE_NORMAL)) + { + //if pretty close to the reachable area + if (dist < 20) EA_Crouch(ms->client); + } //end if + // + dist = BotGapDistance(ms->origin, hordir, ms->entitynum); + // + if (ms->moveflags & MFL_WALK) + { + if (dist > 0) speed = 200 - (180 - 1 * dist); + else speed = 200; + EA_Walk(ms->client); + } //end if + else + { + if (dist > 0) speed = 400 - (360 - 2 * dist); + else speed = 400; + } //end else + //elemantary action move in direction + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_Walk +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir; + float dist, speed; + bot_moveresult_t_cleared( result ); + //if not on the ground and changed areas... don't walk back!! + //(doesn't seem to help) + /* + ms->areanum = BotFuzzyPointReachabilityArea(ms->origin); + if (ms->areanum == reach->areanum) + { +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "BotFinishTravel_Walk: already in reach area\n"); +#endif //DEBUG + return result; + } //end if*/ + //go straight to the reachability end + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + if (dist > 100) dist = 100; + speed = 400 - (400 - 3 * dist); + // + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotFinishTravel_Walk +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_Crouch(bot_movestate_t *ms, aas_reachability_t *reach) +{ + float speed; + vec3_t hordir; + bot_moveresult_t_cleared( result ); + + // + speed = 400; + //walk straight to reachability end + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + // + BotCheckBlocked(ms, hordir, qtrue, &result); + //elemantary actions + EA_Crouch(ms->client); + EA_Move(ms->client, hordir, speed); + // + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_Crouch +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + float dist, speed; + vec3_t hordir; + bot_moveresult_t_cleared( result ); + + //walk straight to reachability start + hordir[0] = reach->start[0] - ms->origin[0]; + hordir[1] = reach->start[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + BotCheckBlocked(ms, hordir, qtrue, &result); + //if pretty close to the barrier + if (dist < 9) + { + EA_Jump(ms->client); + } //end if + else + { + if (dist > 60) dist = 60; + speed = 360 - (360 - 6 * dist); + EA_Move(ms->client, hordir, speed); + } //end else + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_BarrierJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + float dist; + vec3_t hordir; + bot_moveresult_t_cleared( result ); + + //if near the top or going down + if (ms->velocity[2] < 250) + { + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + BotCheckBlocked(ms, hordir, qtrue, &result); + // + EA_Move(ms->client, hordir, 400); + VectorCopy(hordir, result.movedir); + } //end if + // + return result; +} //end of the function BotFinishTravel_BarrierJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_Swim(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t dir; + bot_moveresult_t_cleared( result ); + + //swim straight to reachability end + VectorSubtract(reach->start, ms->origin, dir); + VectorNormalize(dir); + // + BotCheckBlocked(ms, dir, qtrue, &result); + //elemantary actions + EA_Move(ms->client, dir, 400); + // + VectorCopy(dir, result.movedir); + Vector2Angles(dir, result.ideal_viewangles); + result.flags |= MOVERESULT_SWIMVIEW; + // + return result; +} //end of the function BotTravel_Swim +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t dir, hordir; + float dist; + bot_moveresult_t_cleared( result ); + + //swim straight to reachability end + VectorSubtract(reach->end, ms->origin, dir); + VectorCopy(dir, hordir); + hordir[2] = 0; + dir[2] += 15 + crandom() * 40; + //botimport.Print(PRT_MESSAGE, "BotTravel_WaterJump: dir[2] = %f\n", dir[2]); + VectorNormalize(dir); + dist = VectorNormalize(hordir); + //elemantary actions + //EA_Move(ms->client, dir, 400); + EA_MoveForward(ms->client); + //move up if close to the actual out of water jump spot + if (dist < 40) EA_MoveUp(ms->client); + //set the ideal view angles + Vector2Angles(dir, result.ideal_viewangles); + result.flags |= MOVERESULT_MOVEMENTVIEW; + // + VectorCopy(dir, result.movedir); + // + return result; +} //end of the function BotTravel_WaterJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t dir, pnt; + float dist; + bot_moveresult_t_cleared( result ); + + //botimport.Print(PRT_MESSAGE, "BotFinishTravel_WaterJump\n"); + //if waterjumping there's nothing to do + if (ms->moveflags & MFL_WATERJUMP) return result; + //if not touching any water anymore don't do anything + //otherwise the bot sometimes keeps jumping? + VectorCopy(ms->origin, pnt); + pnt[2] -= 32; //extra for q2dm4 near red armor/mega health + if (!(AAS_PointContents(pnt) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return result; + //swim straight to reachability end + VectorSubtract(reach->end, ms->origin, dir); + dir[0] += crandom() * 10; + dir[1] += crandom() * 10; + dir[2] += 70 + crandom() * 10; + dist = VectorNormalize(dir); + //elemantary actions + EA_Move(ms->client, dir, 400); + //set the ideal view angles + Vector2Angles(dir, result.ideal_viewangles); + result.flags |= MOVERESULT_MOVEMENTVIEW; + // + VectorCopy(dir, result.movedir); + // + return result; +} //end of the function BotFinishTravel_WaterJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir, dir; + float dist, speed, reachhordist; + bot_moveresult_t_cleared( result ); + + //check if the bot is blocked by anything + VectorSubtract(reach->start, ms->origin, dir); + VectorNormalize(dir); + BotCheckBlocked(ms, dir, qtrue, &result); + //if the reachability start and end are practially above each other + VectorSubtract(reach->end, reach->start, dir); + dir[2] = 0; + reachhordist = VectorLength(dir); + //walk straight to the reachability start + hordir[0] = reach->start[0] - ms->origin[0]; + hordir[1] = reach->start[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + //if pretty close to the start focus on the reachability end + if (dist < 48) + { + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + // + if (reachhordist < 20) + { + speed = 100; + } //end if + else if (!AAS_HorizontalVelocityForJump(0, reach->start, reach->end, &speed)) + { + speed = 400; + } //end if + } //end if + else + { + if (reachhordist < 20) + { + if (dist > 64) dist = 64; + speed = 400 - (256 - 4 * dist); + } //end if + else + { + speed = 400; + } //end else + } //end else + // + BotCheckBlocked(ms, hordir, qtrue, &result); + //elemantary action + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_WalkOffLedge +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotAirControl(vec3_t origin, vec3_t velocity, vec3_t goal, vec3_t dir, float *speed) +{ + vec3_t org, vel; + float dist; + int i; + + VectorCopy(origin, org); + VectorScale(velocity, 0.1, vel); + for (i = 0; i < 50; i++) + { + vel[2] -= sv_gravity->value * 0.01; + //if going down and next position would be below the goal + if (vel[2] < 0 && org[2] + vel[2] < goal[2]) + { + VectorScale(vel, (goal[2] - org[2]) / vel[2], vel); + VectorAdd(org, vel, org); + VectorSubtract(goal, org, dir); + dist = VectorNormalize(dir); + if (dist > 32) dist = 32; + *speed = 400 - (400 - 13 * dist); + return qtrue; + } //end if + else + { + VectorAdd(org, vel, org); + } //end else + } //end for + VectorSet(dir, 0, 0, 0); + *speed = 400; + return qfalse; +} //end of the function BotAirControl +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t dir, hordir, end, v; + float dist, speed; + bot_moveresult_t_cleared( result ); + + // + VectorSubtract(reach->end, ms->origin, dir); + BotCheckBlocked(ms, dir, qtrue, &result); + // + VectorSubtract(reach->end, ms->origin, v); + v[2] = 0; + dist = VectorNormalize(v); + if (dist > 16) VectorMA(reach->end, 16, v, end); + else VectorCopy(reach->end, end); + // + if (!BotAirControl(ms->origin, ms->velocity, end, hordir, &speed)) + { + //go straight to the reachability end + VectorCopy(dir, hordir); + hordir[2] = 0; + // + dist = VectorNormalize(hordir); + speed = 400; + } //end if + // + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotFinishTravel_WalkOffLedge +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir; + float dist, gapdist, speed, horspeed, sv_jumpvel; + bot_moveresult_t_cleared( result ); + + // + sv_jumpvel = botlibglobals.sv_jumpvel->value; + //walk straight to the reachability start + hordir[0] = reach->start[0] - ms->origin[0]; + hordir[1] = reach->start[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + speed = 350; + // + gapdist = BotGapDistance(ms, hordir, ms->entitynum); + //if pretty close to the start focus on the reachability end + if (dist < 50 || (gapdist && gapdist < 50)) + { + //NOTE: using max speed (400) works best + //if (AAS_HorizontalVelocityForJump(sv_jumpvel, ms->origin, reach->end, &horspeed)) + //{ + // speed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value; + //} //end if + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + VectorNormalize(hordir); + //elemantary action jump + EA_Jump(ms->client); + // + ms->jumpreach = ms->lastreachnum; + speed = 600; + } //end if + else + { + if (AAS_HorizontalVelocityForJump(sv_jumpvel, reach->start, reach->end, &horspeed)) + { + speed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value; + } //end if + } //end else + //elemantary action + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_Jump*/ +/* +bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir, dir1, dir2, mins, maxs, start, end; + float dist1, dist2, speed; + bot_moveresult_t_cleared( result ); + bsp_trace_t trace; + + // + hordir[0] = reach->start[0] - reach->end[0]; + hordir[1] = reach->start[1] - reach->end[1]; + hordir[2] = 0; + VectorNormalize(hordir); + // + VectorCopy(reach->start, start); + start[2] += 1; + //minus back the bouding box size plus 16 + VectorMA(reach->start, 80, hordir, end); + // + AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, mins, maxs); + //check for solids + trace = AAS_Trace(start, mins, maxs, end, ms->entitynum, MASK_PLAYERSOLID); + if (trace.startsolid) VectorCopy(start, trace.endpos); + //check for a gap + for (dist1 = 0; dist1 < 80; dist1 += 10) + { + VectorMA(start, dist1+10, hordir, end); + end[2] += 1; + if (AAS_PointAreaNum(end) != ms->reachareanum) break; + } //end for + if (dist1 < 80) VectorMA(reach->start, dist1, hordir, trace.endpos); +// dist1 = BotGapDistance(start, hordir, ms->entitynum); +// if (dist1 && dist1 <= trace.fraction * 80) VectorMA(reach->start, dist1-20, hordir, trace.endpos); + // + VectorSubtract(ms->origin, reach->start, dir1); + dir1[2] = 0; + dist1 = VectorNormalize(dir1); + VectorSubtract(ms->origin, trace.endpos, dir2); + dir2[2] = 0; + dist2 = VectorNormalize(dir2); + //if just before the reachability start + if (DotProduct(dir1, dir2) < -0.8 || dist2 < 5) + { + //botimport.Print(PRT_MESSAGE, "between jump start and run to point\n"); + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + //elemantary action jump + if (dist1 < 24) EA_Jump(ms->client); + else if (dist1 < 32) EA_DelayedJump(ms->client); + EA_Move(ms->client, hordir, 600); + // + ms->jumpreach = ms->lastreachnum; + } //end if + else + { + //botimport.Print(PRT_MESSAGE, "going towards run to point\n"); + hordir[0] = trace.endpos[0] - ms->origin[0]; + hordir[1] = trace.endpos[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + // + if (dist2 > 80) dist2 = 80; + speed = 400 - (400 - 5 * dist2); + EA_Move(ms->client, hordir, speed); + } //end else + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_Jump*/ +//* +bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir, dir1, dir2, start, end, runstart; +// vec3_t runstart, dir1, dir2, hordir; + float dist1, dist2, speed; + bot_moveresult_t_cleared( result ); + + // + AAS_JumpReachRunStart(reach, runstart); + //* + hordir[0] = runstart[0] - reach->start[0]; + hordir[1] = runstart[1] - reach->start[1]; + hordir[2] = 0; + VectorNormalize(hordir); + // + VectorCopy(reach->start, start); + start[2] += 1; + VectorMA(reach->start, 80, hordir, runstart); + //check for a gap + for (dist1 = 0; dist1 < 80; dist1 += 10) + { + VectorMA(start, dist1+10, hordir, end); + end[2] += 1; + if (AAS_PointAreaNum(end) != ms->reachareanum) break; + } //end for + if (dist1 < 80) VectorMA(reach->start, dist1, hordir, runstart); + // + VectorSubtract(ms->origin, reach->start, dir1); + dir1[2] = 0; + dist1 = VectorNormalize(dir1); + VectorSubtract(ms->origin, runstart, dir2); + dir2[2] = 0; + dist2 = VectorNormalize(dir2); + //if just before the reachability start + if (DotProduct(dir1, dir2) < -0.8 || dist2 < 5) + { +// botimport.Print(PRT_MESSAGE, "between jump start and run start point\n"); + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + //elemantary action jump + if (dist1 < 24) EA_Jump(ms->client); + else if (dist1 < 32) EA_DelayedJump(ms->client); + EA_Move(ms->client, hordir, 600); + // + ms->jumpreach = ms->lastreachnum; + } //end if + else + { +// botimport.Print(PRT_MESSAGE, "going towards run start point\n"); + hordir[0] = runstart[0] - ms->origin[0]; + hordir[1] = runstart[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + // + if (dist2 > 80) dist2 = 80; + speed = 400 - (400 - 5 * dist2); + EA_Move(ms->client, hordir, speed); + } //end else + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_Jump*/ +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir, hordir2; + float speed, dist; + bot_moveresult_t_cleared( result ); + + //if not jumped yet + if (!ms->jumpreach) return result; + //go straight to the reachability end + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + hordir2[0] = reach->end[0] - reach->start[0]; + hordir2[1] = reach->end[1] - reach->start[1]; + hordir2[2] = 0; + VectorNormalize(hordir2); + // + if (DotProduct(hordir, hordir2) < -0.5 && dist < 24) return result; + //always use max speed when traveling through the air + speed = 800; + // + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotFinishTravel_Jump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_Ladder(bot_movestate_t *ms, aas_reachability_t *reach) +{ + //float dist, speed; + vec3_t dir, viewdir;//, hordir; + vec3_t origin = {0, 0, 0}; +// vec3_t up = {0, 0, 1}; + bot_moveresult_t_cleared( result ); + + // +// if ((ms->moveflags & MFL_AGAINSTLADDER)) + //NOTE: not a good idea for ladders starting in water + // || !(ms->moveflags & MFL_ONGROUND)) + { + //botimport.Print(PRT_MESSAGE, "against ladder or not on ground\n"); + VectorSubtract(reach->end, ms->origin, dir); + VectorNormalize(dir); + //set the ideal view angles, facing the ladder up or down + viewdir[0] = dir[0]; + viewdir[1] = dir[1]; + viewdir[2] = 3 * dir[2]; + Vector2Angles(viewdir, result.ideal_viewangles); + //elemantary action + EA_Move(ms->client, origin, 0); + EA_MoveForward(ms->client); + //set movement view flag so the AI can see the view is focussed + result.flags |= MOVERESULT_MOVEMENTVIEW; + } //end if +/* else + { + //botimport.Print(PRT_MESSAGE, "moving towards ladder\n"); + VectorSubtract(reach->end, ms->origin, dir); + //make sure the horizontal movement is large anough + VectorCopy(dir, hordir); + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + dir[0] = hordir[0]; + dir[1] = hordir[1]; + if (dir[2] > 0) dir[2] = 1; + else dir[2] = -1; + if (dist > 50) dist = 50; + speed = 400 - (200 - 4 * dist); + EA_Move(ms->client, dir, speed); + } //end else*/ + //save the movement direction + VectorCopy(dir, result.movedir); + // + return result; +} //end of the function BotTravel_Ladder +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_Teleport(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir; + float dist; + bot_moveresult_t_cleared( result ); + + //if the bot is being teleported + if (ms->moveflags & MFL_TELEPORTED) return result; + + //walk straight to center of the teleporter + VectorSubtract(reach->start, ms->origin, hordir); + if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0; + dist = VectorNormalize(hordir); + // + BotCheckBlocked(ms, hordir, qtrue, &result); + + if (dist < 30) EA_Move(ms->client, hordir, 200); + else EA_Move(ms->client, hordir, 400); + + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + + VectorCopy(hordir, result.movedir); + return result; +} //end of the function BotTravel_Teleport +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t dir, dir1, dir2, hordir, bottomcenter; + float dist, dist1, dist2, speed; + bot_moveresult_t_cleared( result ); + + //if standing on the plat + if (BotOnMover(ms->origin, ms->entitynum, reach)) + { +#ifdef DEBUG_ELEVATOR + botimport.Print(PRT_MESSAGE, "bot on elevator\n"); +#endif //DEBUG_ELEVATOR + //if vertically not too far from the end point + if (abs(ms->origin[2] - reach->end[2]) < sv_maxbarrier->value) + { +#ifdef DEBUG_ELEVATOR + botimport.Print(PRT_MESSAGE, "bot moving to end\n"); +#endif //DEBUG_ELEVATOR + //move to the end point + VectorSubtract(reach->end, ms->origin, hordir); + hordir[2] = 0; + VectorNormalize(hordir); + if (!BotCheckBarrierJump(ms, hordir, 100)) + { + EA_Move(ms->client, hordir, 400); + } //end if + VectorCopy(hordir, result.movedir); + } //end else + //if not really close to the center of the elevator + else + { + MoverBottomCenter(reach, bottomcenter); + VectorSubtract(bottomcenter, ms->origin, hordir); + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + if (dist > 10) + { +#ifdef DEBUG_ELEVATOR + botimport.Print(PRT_MESSAGE, "bot moving to center\n"); +#endif //DEBUG_ELEVATOR + //move to the center of the plat + if (dist > 100) dist = 100; + speed = 400 - (400 - 4 * dist); + // + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + } //end if + } //end else + } //end if + else + { +#ifdef DEBUG_ELEVATOR + botimport.Print(PRT_MESSAGE, "bot not on elevator\n"); +#endif //DEBUG_ELEVATOR + //if very near the reachability end + VectorSubtract(reach->end, ms->origin, dir); + dist = VectorLength(dir); + if (dist < 64) + { + if (dist > 60) dist = 60; + speed = 360 - (360 - 6 * dist); + // + if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50)) + { + if (speed > 5) EA_Move(ms->client, dir, speed); + } //end if + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + //stop using this reachability + ms->reachability_time = 0; + return result; + } //end if + //get direction and distance to reachability start + VectorSubtract(reach->start, ms->origin, dir1); + if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0; + dist1 = VectorNormalize(dir1); + //if the elevator isn't down + if (!MoverDown(reach)) + { +#ifdef DEBUG_ELEVATOR + botimport.Print(PRT_MESSAGE, "elevator not down\n"); +#endif //DEBUG_ELEVATOR + dist = dist1; + VectorCopy(dir1, dir); + // + BotCheckBlocked(ms, dir, qfalse, &result); + // + if (dist > 60) dist = 60; + speed = 360 - (360 - 6 * dist); + // + if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) + { + if (speed > 5) EA_Move(ms->client, dir, speed); + } //end if + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + //this isn't a failure... just wait till the elevator comes down + result.type = RESULTTYPE_ELEVATORUP; + result.flags |= MOVERESULT_WAITING; + return result; + } //end if + //get direction and distance to elevator bottom center + MoverBottomCenter(reach, bottomcenter); + VectorSubtract(bottomcenter, ms->origin, dir2); + if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0; + dist2 = VectorNormalize(dir2); + //if very close to the reachability start or + //closer to the elevator center or + //between reachability start and elevator center + if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0) + { +#ifdef DEBUG_ELEVATOR + botimport.Print(PRT_MESSAGE, "bot moving to center\n"); +#endif //DEBUG_ELEVATOR + dist = dist2; + VectorCopy(dir2, dir); + } //end if + else //closer to the reachability start + { +#ifdef DEBUG_ELEVATOR + botimport.Print(PRT_MESSAGE, "bot moving to start\n"); +#endif //DEBUG_ELEVATOR + dist = dist1; + VectorCopy(dir1, dir); + } //end else + // + BotCheckBlocked(ms, dir, qfalse, &result); + // + if (dist > 60) dist = 60; + speed = 400 - (400 - 6 * dist); + // + if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) + { + EA_Move(ms->client, dir, speed); + } //end if + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + } //end else + return result; +} //end of the function BotTravel_Elevator +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t bottomcenter, bottomdir, topdir; + bot_moveresult_t_cleared( result ); + + // + MoverBottomCenter(reach, bottomcenter); + VectorSubtract(bottomcenter, ms->origin, bottomdir); + // + VectorSubtract(reach->end, ms->origin, topdir); + // + if (fabs(bottomdir[2]) < fabs(topdir[2])) + { + VectorNormalize(bottomdir); + EA_Move(ms->client, bottomdir, 300); + } //end if + else + { + VectorNormalize(topdir); + EA_Move(ms->client, topdir, 300); + } //end else + return result; +} //end of the function BotFinishTravel_Elevator +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFuncBobStartEnd(aas_reachability_t *reach, vec3_t start, vec3_t end, vec3_t origin) +{ + int spawnflags, modelnum; + vec3_t mins, maxs, mid, angles = {0, 0, 0}; + int num0, num1; + + modelnum = reach->facenum & 0x0000FFFF; + if (!AAS_OriginOfMoverWithModelNum(modelnum, origin)) + { + botimport.Print(PRT_MESSAGE, "BotFuncBobStartEnd: no entity with model %d\n", modelnum); + VectorSet(start, 0, 0, 0); + VectorSet(end, 0, 0, 0); + return; + } //end if + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL); + VectorAdd(mins, maxs, mid); + VectorScale(mid, 0.5, mid); + VectorCopy(mid, start); + VectorCopy(mid, end); + spawnflags = reach->facenum >> 16; + num0 = reach->edgenum >> 16; + if (num0 > 0x00007FFF) num0 |= 0xFFFF0000; + num1 = reach->edgenum & 0x0000FFFF; + if (num1 > 0x00007FFF) num1 |= 0xFFFF0000; + if (spawnflags & 1) + { + start[0] = num0; + end[0] = num1; + // + origin[0] += mid[0]; + origin[1] = mid[1]; + origin[2] = mid[2]; + } //end if + else if (spawnflags & 2) + { + start[1] = num0; + end[1] = num1; + // + origin[0] = mid[0]; + origin[1] += mid[1]; + origin[2] = mid[2]; + } //end else if + else + { + start[2] = num0; + end[2] = num1; + // + origin[0] = mid[0]; + origin[1] = mid[1]; + origin[2] += mid[2]; + } //end else +} //end of the function BotFuncBobStartEnd +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t dir, dir1, dir2, hordir, bottomcenter, bob_start, bob_end, bob_origin; + float dist, dist1, dist2, speed; + bot_moveresult_t_cleared( result ); + + // + BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin); + //if standing ontop of the func_bobbing + if (BotOnMover(ms->origin, ms->entitynum, reach)) + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "bot on func_bobbing\n"); +#endif + //if near end point of reachability + VectorSubtract(bob_origin, bob_end, dir); + if (VectorLength(dir) < 24) + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "bot moving to reachability end\n"); +#endif + //move to the end point + VectorSubtract(reach->end, ms->origin, hordir); + hordir[2] = 0; + VectorNormalize(hordir); + if (!BotCheckBarrierJump(ms, hordir, 100)) + { + EA_Move(ms->client, hordir, 400); + } //end if + VectorCopy(hordir, result.movedir); + } //end else + //if not really close to the center of the elevator + else + { + MoverBottomCenter(reach, bottomcenter); + VectorSubtract(bottomcenter, ms->origin, hordir); + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + if (dist > 10) + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n"); +#endif + //move to the center of the plat + if (dist > 100) dist = 100; + speed = 400 - (400 - 4 * dist); + // + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + } //end if + } //end else + } //end if + else + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "bot not ontop of func_bobbing\n"); +#endif + //if very near the reachability end + VectorSubtract(reach->end, ms->origin, dir); + dist = VectorLength(dir); + if (dist < 64) + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "bot moving to end\n"); +#endif + if (dist > 60) dist = 60; + speed = 360 - (360 - 6 * dist); + //if swimming or no barrier jump + if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50)) + { + if (speed > 5) EA_Move(ms->client, dir, speed); + } //end if + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + //stop using this reachability + ms->reachability_time = 0; + return result; + } //end if + //get direction and distance to reachability start + VectorSubtract(reach->start, ms->origin, dir1); + if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0; + dist1 = VectorNormalize(dir1); + //if func_bobbing is Not it's start position + VectorSubtract(bob_origin, bob_start, dir); + if (VectorLength(dir) > 16) + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "func_bobbing not at start\n"); +#endif + dist = dist1; + VectorCopy(dir1, dir); + // + BotCheckBlocked(ms, dir, qfalse, &result); + // + if (dist > 60) dist = 60; + speed = 360 - (360 - 6 * dist); + // + if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) + { + if (speed > 5) EA_Move(ms->client, dir, speed); + } //end if + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + //this isn't a failure... just wait till the func_bobbing arrives + result.type = RESULTTYPE_WAITFORFUNCBOBBING; + result.flags |= MOVERESULT_WAITING; + return result; + } //end if + //get direction and distance to func_bob bottom center + MoverBottomCenter(reach, bottomcenter); + VectorSubtract(bottomcenter, ms->origin, dir2); + if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0; + dist2 = VectorNormalize(dir2); + //if very close to the reachability start or + //closer to the elevator center or + //between reachability start and func_bobbing center + if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0) + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n"); +#endif + dist = dist2; + VectorCopy(dir2, dir); + } //end if + else //closer to the reachability start + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "bot moving to reachability start\n"); +#endif + dist = dist1; + VectorCopy(dir1, dir); + } //end else + // + BotCheckBlocked(ms, dir, qfalse, &result); + // + if (dist > 60) dist = 60; + speed = 400 - (400 - 6 * dist); + // + if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) + { + EA_Move(ms->client, dir, speed); + } //end if + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + } //end else + return result; +} //end of the function BotTravel_FuncBobbing +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t bob_origin, bob_start, bob_end, dir, hordir, bottomcenter; + bot_moveresult_t_cleared( result ); + float dist, speed; + + // + BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin); + // + VectorSubtract(bob_origin, bob_end, dir); + dist = VectorLength(dir); + //if the func_bobbing is near the end + if (dist < 16) + { + VectorSubtract(reach->end, ms->origin, hordir); + if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0; + dist = VectorNormalize(hordir); + // + if (dist > 60) dist = 60; + speed = 360 - (360 - 6 * dist); + // + if (speed > 5) EA_Move(ms->client, dir, speed); + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + } //end if + else + { + MoverBottomCenter(reach, bottomcenter); + VectorSubtract(bottomcenter, ms->origin, hordir); + if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0; + dist = VectorNormalize(hordir); + // + if (dist > 5) + { + //move to the center of the plat + if (dist > 100) dist = 100; + speed = 400 - (400 - 4 * dist); + // + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + } //end if + } //end else + return result; +} //end of the function BotFinishTravel_FuncBobbing +//=========================================================================== +// 0 no valid grapple hook visible +// 1 the grapple hook is still flying +// 2 the grapple hooked into a wall +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int GrappleState(bot_movestate_t *ms, aas_reachability_t *reach) +{ + int i; + aas_entityinfo_t entinfo; + + //if the grapple hook is pulling + if (ms->moveflags & MFL_GRAPPLEPULL) + return 2; + //check for a visible grapple missile entity + //or visible grapple entity + for (i = AAS_NextEntity(0); i; i = AAS_NextEntity(i)) + { + if (AAS_EntityType(i) == (int) entitytypemissile->value) + { + AAS_EntityInfo(i, &entinfo); + if (entinfo.weapon == (int) weapindex_grapple->value) + { + return 1; + } //end if + } //end if + } //end for + //no valid grapple at all + return 0; +} //end of the function GrappleState +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetGrapple(bot_movestate_t *ms) +{ + aas_reachability_t reach; + + AAS_ReachabilityFromNum(ms->lastreachnum, &reach); + //if not using the grapple hook reachability anymore + if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_GRAPPLEHOOK) + { + if ((ms->moveflags & MFL_ACTIVEGRAPPLE) || ms->grapplevisible_time) + { + if (offhandgrapple->value) + EA_Command(ms->client, cmd_grappleoff->string); + ms->moveflags &= ~MFL_ACTIVEGRAPPLE; + ms->grapplevisible_time = 0; +#ifdef DEBUG_GRAPPLE + botimport.Print(PRT_MESSAGE, "reset grapple\n"); +#endif //DEBUG_GRAPPLE + } //end if + } //end if +} //end of the function BotResetGrapple +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_Grapple(bot_movestate_t *ms, aas_reachability_t *reach) +{ + bot_moveresult_t_cleared( result ); + float dist, speed; + vec3_t dir, viewdir, org; + int state, areanum; + bsp_trace_t trace; + +#ifdef DEBUG_GRAPPLE + static int debugline; + if (!debugline) debugline = botimport.DebugLineCreate(); + botimport.DebugLineShow(debugline, reach->start, reach->end, LINECOLOR_BLUE); +#endif //DEBUG_GRAPPLE + + // + if (ms->moveflags & MFL_GRAPPLERESET) + { + if (offhandgrapple->value) + EA_Command(ms->client, cmd_grappleoff->string); + ms->moveflags &= ~MFL_ACTIVEGRAPPLE; + return result; + } //end if + // + if (!(int) offhandgrapple->value) + { + result.weapon = weapindex_grapple->value; + result.flags |= MOVERESULT_MOVEMENTWEAPON; + } //end if + // + if (ms->moveflags & MFL_ACTIVEGRAPPLE) + { +#ifdef DEBUG_GRAPPLE + botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: active grapple\n"); +#endif //DEBUG_GRAPPLE + // + state = GrappleState(ms, reach); + // + VectorSubtract(reach->end, ms->origin, dir); + dir[2] = 0; + dist = VectorLength(dir); + //if very close to the grapple end or the grappled is hooked and + //the bot doesn't get any closer + if (state && dist < 48) + { + if (ms->lastgrappledist - dist < 1) + { +#ifdef DEBUG_GRAPPLE + botimport.Print(PRT_ERROR, "grapple normal end\n"); +#endif //DEBUG_GRAPPLE + if (offhandgrapple->value) + EA_Command(ms->client, cmd_grappleoff->string); + ms->moveflags &= ~MFL_ACTIVEGRAPPLE; + ms->moveflags |= MFL_GRAPPLERESET; + ms->reachability_time = 0; //end the reachability + return result; + } //end if + } //end if + //if no valid grapple at all, or the grapple hooked and the bot + //isn't moving anymore + else if (!state || (state == 2 && dist > ms->lastgrappledist - 2)) + { + if (ms->grapplevisible_time < AAS_Time() - 0.4) + { +#ifdef DEBUG_GRAPPLE + botimport.Print(PRT_ERROR, "grapple not visible\n"); +#endif //DEBUG_GRAPPLE + if (offhandgrapple->value) + EA_Command(ms->client, cmd_grappleoff->string); + ms->moveflags &= ~MFL_ACTIVEGRAPPLE; + ms->moveflags |= MFL_GRAPPLERESET; + ms->reachability_time = 0; //end the reachability + return result; + } //end if + } //end if + else + { + ms->grapplevisible_time = AAS_Time(); + } //end else + // + if (!(int) offhandgrapple->value) + { + EA_Attack(ms->client); + } //end if + //remember the current grapple distance + ms->lastgrappledist = dist; + } //end if + else + { +#ifdef DEBUG_GRAPPLE + botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: inactive grapple\n"); +#endif //DEBUG_GRAPPLE + // + ms->grapplevisible_time = AAS_Time(); + // + VectorSubtract(reach->start, ms->origin, dir); + if (!(ms->moveflags & MFL_SWIMMING)) dir[2] = 0; + VectorAdd(ms->origin, ms->viewoffset, org); + VectorSubtract(reach->end, org, viewdir); + // + dist = VectorNormalize(dir); + Vector2Angles(viewdir, result.ideal_viewangles); + result.flags |= MOVERESULT_MOVEMENTVIEW; + // + if (dist < 5 && + fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 2 && + fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 2) + { +#ifdef DEBUG_GRAPPLE + botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: activating grapple\n"); +#endif //DEBUG_GRAPPLE + //check if the grapple missile path is clear + VectorAdd(ms->origin, ms->viewoffset, org); + trace = AAS_Trace(org, NULL, NULL, reach->end, ms->entitynum, CONTENTS_SOLID); + VectorSubtract(reach->end, trace.endpos, dir); + if (VectorLength(dir) > 16) + { + result.failure = qtrue; + return result; + } //end if + //activate the grapple + if (offhandgrapple->value) + { + EA_Command(ms->client, cmd_grappleon->string); + } //end if + else + { + EA_Attack(ms->client); + } //end else + ms->moveflags |= MFL_ACTIVEGRAPPLE; + ms->lastgrappledist = 999999; + } //end if + else + { + if (dist < 70) speed = 300 - (300 - 4 * dist); + else speed = 400; + // + BotCheckBlocked(ms, dir, qtrue, &result); + //elemantary action move in direction + EA_Move(ms->client, dir, speed); + VectorCopy(dir, result.movedir); + } //end else + //if in another area before actually grappling + areanum = AAS_PointAreaNum(ms->origin); + if (areanum && areanum != ms->reachareanum) ms->reachability_time = 0; + } //end else + return result; +} //end of the function BotTravel_Grapple +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_RocketJump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir; + float dist, speed; + bot_moveresult_t_cleared( result ); + + //botimport.Print(PRT_MESSAGE, "BotTravel_RocketJump: bah\n"); + // + hordir[0] = reach->start[0] - ms->origin[0]; + hordir[1] = reach->start[1] - ms->origin[1]; + hordir[2] = 0; + // + dist = VectorNormalize(hordir); + //look in the movement direction + Vector2Angles(hordir, result.ideal_viewangles); + //look straight down + result.ideal_viewangles[PITCH] = 90; + // + if (dist < 5 && + fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 && + fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5) + { + //botimport.Print(PRT_MESSAGE, "between jump start and run start point\n"); + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + //elemantary action jump + EA_Jump(ms->client); + EA_Attack(ms->client); + EA_Move(ms->client, hordir, 800); + // + ms->jumpreach = ms->lastreachnum; + } //end if + else + { + if (dist > 80) dist = 80; + speed = 400 - (400 - 5 * dist); + EA_Move(ms->client, hordir, speed); + } //end else + //look in the movement direction + Vector2Angles(hordir, result.ideal_viewangles); + //look straight down + result.ideal_viewangles[PITCH] = 90; + //set the view angles directly + EA_View(ms->client, result.ideal_viewangles); + //view is important for the movment + result.flags |= MOVERESULT_MOVEMENTVIEWSET; + //select the rocket launcher + EA_SelectWeapon(ms->client, (int) weapindex_rocketlauncher->value); + //weapon is used for movement + result.weapon = (int) weapindex_rocketlauncher->value; + result.flags |= MOVERESULT_MOVEMENTWEAPON; + // + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_RocketJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_BFGJump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir; + float dist, speed; + bot_moveresult_t_cleared( result ); + + //botimport.Print(PRT_MESSAGE, "BotTravel_BFGJump: bah\n"); + // + hordir[0] = reach->start[0] - ms->origin[0]; + hordir[1] = reach->start[1] - ms->origin[1]; + hordir[2] = 0; + // + dist = VectorNormalize(hordir); + // + if (dist < 5 && + fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 && + fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5) + { + //botimport.Print(PRT_MESSAGE, "between jump start and run start point\n"); + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + //elemantary action jump + EA_Jump(ms->client); + EA_Attack(ms->client); + EA_Move(ms->client, hordir, 800); + // + ms->jumpreach = ms->lastreachnum; + } //end if + else + { + if (dist > 80) dist = 80; + speed = 400 - (400 - 5 * dist); + EA_Move(ms->client, hordir, speed); + } //end else + //look in the movement direction + Vector2Angles(hordir, result.ideal_viewangles); + //look straight down + result.ideal_viewangles[PITCH] = 90; + //set the view angles directly + EA_View(ms->client, result.ideal_viewangles); + //view is important for the movment + result.flags |= MOVERESULT_MOVEMENTVIEWSET; + //select the rocket launcher + EA_SelectWeapon(ms->client, (int) weapindex_bfg10k->value); + //weapon is used for movement + result.weapon = (int) weapindex_bfg10k->value; + result.flags |= MOVERESULT_MOVEMENTWEAPON; + // + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_BFGJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_WeaponJump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir; + float speed; + bot_moveresult_t_cleared( result ); + + //if not jumped yet + if (!ms->jumpreach) return result; + /* + //go straight to the reachability end + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + //always use max speed when traveling through the air + EA_Move(ms->client, hordir, 800); + VectorCopy(hordir, result.movedir); + */ + // + if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed)) + { + //go straight to the reachability end + VectorSubtract(reach->end, ms->origin, hordir); + hordir[2] = 0; + VectorNormalize(hordir); + speed = 400; + } //end if + // + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotFinishTravel_WeaponJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach) +{ + float dist, speed; + vec3_t hordir; + bot_moveresult_t_cleared( result ); + + //first walk straight to the reachability start + hordir[0] = reach->start[0] - ms->origin[0]; + hordir[1] = reach->start[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + BotCheckBlocked(ms, hordir, qtrue, &result); + speed = 400; + //elemantary action move in direction + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_JumpPad +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach) +{ + float speed; + vec3_t hordir; + bot_moveresult_t_cleared( result ); + + if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed)) + { + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + speed = 400; + } //end if + BotCheckBlocked(ms, hordir, qtrue, &result); + //elemantary action move in direction + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotFinishTravel_JumpPad +//=========================================================================== +// time before the reachability times out +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotReachabilityTime(aas_reachability_t *reach) +{ + switch(reach->traveltype & TRAVELTYPE_MASK) + { + case TRAVEL_WALK: return 5; + case TRAVEL_CROUCH: return 5; + case TRAVEL_BARRIERJUMP: return 5; + case TRAVEL_LADDER: return 6; + case TRAVEL_WALKOFFLEDGE: return 5; + case TRAVEL_JUMP: return 5; + case TRAVEL_SWIM: return 5; + case TRAVEL_WATERJUMP: return 5; + case TRAVEL_TELEPORT: return 5; + case TRAVEL_ELEVATOR: return 10; + case TRAVEL_GRAPPLEHOOK: return 8; + case TRAVEL_ROCKETJUMP: return 6; + case TRAVEL_BFGJUMP: return 6; + case TRAVEL_JUMPPAD: return 10; + case TRAVEL_FUNCBOB: return 10; + default: + { + botimport.Print(PRT_ERROR, "travel type %d not implemented yet\n", reach->traveltype); + return 8; + } //end case + } //end switch +} //end of the function BotReachabilityTime +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotMoveInGoalArea(bot_movestate_t *ms, bot_goal_t *goal) +{ + bot_moveresult_t_cleared( result ); + vec3_t dir; + float dist, speed; + +#ifdef DEBUG + //botimport.Print(PRT_MESSAGE, "%s: moving straight to goal\n", ClientName(ms->entitynum-1)); + //AAS_ClearShownDebugLines(); + //AAS_DebugLine(ms->origin, goal->origin, LINECOLOR_RED); +#endif //DEBUG + //walk straight to the goal origin + dir[0] = goal->origin[0] - ms->origin[0]; + dir[1] = goal->origin[1] - ms->origin[1]; + if (ms->moveflags & MFL_SWIMMING) + { + dir[2] = goal->origin[2] - ms->origin[2]; + result.traveltype = TRAVEL_SWIM; + } //end if + else + { + dir[2] = 0; + result.traveltype = TRAVEL_WALK; + } //endif + // + dist = VectorNormalize(dir); + if (dist > 100) dist = 100; + speed = 400 - (400 - 4 * dist); + if (speed < 10) speed = 0; + // + BotCheckBlocked(ms, dir, qtrue, &result); + //elemantary action move in direction + EA_Move(ms->client, dir, speed); + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) + { + Vector2Angles(dir, result.ideal_viewangles); + result.flags |= MOVERESULT_SWIMVIEW; + } //end if + //if (!debugline) debugline = botimport.DebugLineCreate(); + //botimport.DebugLineShow(debugline, ms->origin, goal->origin, LINECOLOR_BLUE); + // + ms->lastreachnum = 0; + ms->lastareanum = 0; + ms->lastgoalareanum = goal->areanum; + VectorCopy(ms->origin, ms->lastorigin); + // + return result; +} //end of the function BotMoveInGoalArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, int travelflags) +{ + int reachnum, lastreachnum, foundjumppad, ent, resultflags; + aas_reachability_t reach, lastreach; + bot_movestate_t *ms; + //vec3_t mins, maxs, up = {0, 0, 1}; + //bsp_trace_t trace; + //static int debugline; + + result->failure = qfalse; + result->type = 0; + result->blocked = qfalse; + result->blockentity = 0; + result->traveltype = 0; + result->flags = 0; + + // + ms = BotMoveStateFromHandle(movestate); + if (!ms) return; + //reset the grapple before testing if the bot has a valid goal + //because the bot could loose all it's goals when stuck to a wall + BotResetGrapple(ms); + // + if (!goal) + { +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "client %d: movetogoal -> no goal\n", ms->client); +#endif //DEBUG + result->failure = qtrue; + return; + } //end if + //botimport.Print(PRT_MESSAGE, "numavoidreach = %d\n", ms->numavoidreach); + //remove some of the move flags + ms->moveflags &= ~(MFL_SWIMMING|MFL_AGAINSTLADDER); + //set some of the move flags + //NOTE: the MFL_ONGROUND flag is also set in the higher AI + if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND; + // + if (ms->moveflags & MFL_ONGROUND) + { + int modeltype, modelnum; + + ent = BotOnTopOfEntity(ms); + + if (ent != -1) + { + modelnum = AAS_EntityModelindex(ent); + if (modelnum >= 0 && modelnum < MAX_MODELS) + { + modeltype = modeltypes[modelnum]; + + if (modeltype == MODELTYPE_FUNC_PLAT) + { + AAS_ReachabilityFromNum(ms->lastreachnum, &reach); + //if the bot is Not using the elevator + if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR || + //NOTE: the face number is the plat model number + (reach.facenum & 0x0000FFFF) != modelnum) + { + reachnum = AAS_NextModelReachability(0, modelnum); + if (reachnum) + { + //botimport.Print(PRT_MESSAGE, "client %d: accidentally ended up on func_plat\n", ms->client); + AAS_ReachabilityFromNum(reachnum, &reach); + ms->lastreachnum = reachnum; + ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach); + } //end if + else + { + if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "client %d: on func_plat without reachability\n", ms->client); + } //end if + result->blocked = qtrue; + result->blockentity = ent; + result->flags |= MOVERESULT_ONTOPOFOBSTACLE; + return; + } //end else + } //end if + result->flags |= MOVERESULT_ONTOPOF_ELEVATOR; + } //end if + else if (modeltype == MODELTYPE_FUNC_BOB) + { + AAS_ReachabilityFromNum(ms->lastreachnum, &reach); + //if the bot is Not using the func bobbing + if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB || + //NOTE: the face number is the func_bobbing model number + (reach.facenum & 0x0000FFFF) != modelnum) + { + reachnum = AAS_NextModelReachability(0, modelnum); + if (reachnum) + { + //botimport.Print(PRT_MESSAGE, "client %d: accidentally ended up on func_bobbing\n", ms->client); + AAS_ReachabilityFromNum(reachnum, &reach); + ms->lastreachnum = reachnum; + ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach); + } //end if + else + { + if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "client %d: on func_bobbing without reachability\n", ms->client); + } //end if + result->blocked = qtrue; + result->blockentity = ent; + result->flags |= MOVERESULT_ONTOPOFOBSTACLE; + return; + } //end else + } //end if + result->flags |= MOVERESULT_ONTOPOF_FUNCBOB; + } //end if + else if (modeltype == MODELTYPE_FUNC_STATIC || modeltype == MODELTYPE_FUNC_DOOR) + { + // check if ontop of a door bridge ? + ms->areanum = BotFuzzyPointReachabilityArea(ms->origin); + // if not in a reachability area + if (!AAS_AreaReachability(ms->areanum)) + { + result->blocked = qtrue; + result->blockentity = ent; + result->flags |= MOVERESULT_ONTOPOFOBSTACLE; + return; + } //end if + } //end else if + else + { + result->blocked = qtrue; + result->blockentity = ent; + result->flags |= MOVERESULT_ONTOPOFOBSTACLE; + return; + } //end else + } //end if + } //end if + } //end if + //if swimming + if (AAS_Swimming(ms->origin)) ms->moveflags |= MFL_SWIMMING; + //if against a ladder + if (AAS_AgainstLadder(ms->origin)) ms->moveflags |= MFL_AGAINSTLADDER; + //if the bot is on the ground, swimming or against a ladder + if (ms->moveflags & (MFL_ONGROUND|MFL_SWIMMING|MFL_AGAINSTLADDER)) + { + //botimport.Print(PRT_MESSAGE, "%s: onground, swimming or against ladder\n", ClientName(ms->entitynum-1)); + // + AAS_ReachabilityFromNum(ms->lastreachnum, &lastreach); + //reachability area the bot is in + //ms->areanum = BotReachabilityArea(ms->origin, ((lastreach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR)); + ms->areanum = BotFuzzyPointReachabilityArea(ms->origin); + // + if ( !ms->areanum ) + { + result->failure = qtrue; + result->blocked = qtrue; + result->blockentity = 0; + result->type = RESULTTYPE_INSOLIDAREA; + return; + } //end if + //if the bot is in the goal area + if (ms->areanum == goal->areanum) + { + *result = BotMoveInGoalArea(ms, goal); + return; + } //end if + //assume we can use the reachability from the last frame + reachnum = ms->lastreachnum; + //if there is a last reachability + if (reachnum) + { + AAS_ReachabilityFromNum(reachnum, &reach); + //check if the reachability is still valid + if (!(AAS_TravelFlagForType(reach.traveltype) & travelflags)) + { + reachnum = 0; + } //end if + //special grapple hook case + else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_GRAPPLEHOOK) + { + if (ms->reachability_time < AAS_Time() || + (ms->moveflags & MFL_GRAPPLERESET)) + { + reachnum = 0; + } //end if + } //end if + //special elevator case + else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR || + (reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB) + { + if ((result->flags & MOVERESULT_ONTOPOF_FUNCBOB) || + (result->flags & MOVERESULT_ONTOPOF_FUNCBOB)) + { + ms->reachability_time = AAS_Time() + 5; + } //end if + //if the bot was going for an elevator and reached the reachability area + if (ms->areanum == reach.areanum || + ms->reachability_time < AAS_Time()) + { + reachnum = 0; + } //end if + } //end if + else + { +#ifdef DEBUG + if (bot_developer) + { + if (ms->reachability_time < AAS_Time()) + { + botimport.Print(PRT_MESSAGE, "client %d: reachability timeout in ", ms->client); + AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); + botimport.Print(PRT_MESSAGE, "\n"); + } //end if + /* + if (ms->lastareanum != ms->areanum) + { + botimport.Print(PRT_MESSAGE, "changed from area %d to %d\n", ms->lastareanum, ms->areanum); + } //end if*/ + } //end if +#endif //DEBUG + //if the goal area changed or the reachability timed out + //or the area changed + if (ms->lastgoalareanum != goal->areanum || + ms->reachability_time < AAS_Time() || + ms->lastareanum != ms->areanum) + { + reachnum = 0; + //botimport.Print(PRT_MESSAGE, "area change or timeout\n"); + } //end else if + } //end else + } //end if + resultflags = 0; + //if the bot needs a new reachability + if (!reachnum) + { + //if the area has no reachability links + if (!AAS_AreaReachability(ms->areanum)) + { +#ifdef DEBUG + if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "area %d no reachability\n", ms->areanum); + } //end if +#endif //DEBUG + } //end if + //get a new reachability leading towards the goal + reachnum = BotGetReachabilityToGoal(ms->origin, ms->areanum, + ms->lastgoalareanum, ms->lastareanum, + ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries, + goal, travelflags, travelflags, + ms->avoidspots, ms->numavoidspots, &resultflags); + //the area number the reachability starts in + ms->reachareanum = ms->areanum; + //reset some state variables + ms->jumpreach = 0; //for TRAVEL_JUMP + ms->moveflags &= ~MFL_GRAPPLERESET; //for TRAVEL_GRAPPLEHOOK + //if there is a reachability to the goal + if (reachnum) + { + AAS_ReachabilityFromNum(reachnum, &reach); + //set a timeout for this reachability + ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach); + // +#ifdef AVOIDREACH + //add the reachability to the reachabilities to avoid for a while + BotAddToAvoidReach(ms, reachnum, AVOIDREACH_TIME); +#endif //AVOIDREACH + } //end if +#ifdef DEBUG + + else if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "goal not reachable\n"); + Com_Memset(&reach, 0, sizeof(aas_reachability_t)); //make compiler happy + } //end else + if (bot_developer) + { + //if still going for the same goal + if (ms->lastgoalareanum == goal->areanum) + { + if (ms->lastareanum == reach.areanum) + { + botimport.Print(PRT_MESSAGE, "same goal, going back to previous area\n"); + } //end if + } //end if + } //end if +#endif //DEBUG + } //end else + // + ms->lastreachnum = reachnum; + ms->lastgoalareanum = goal->areanum; + ms->lastareanum = ms->areanum; + //if the bot has a reachability + if (reachnum) + { + //get the reachability from the number + AAS_ReachabilityFromNum(reachnum, &reach); + result->traveltype = reach.traveltype; + // +#ifdef DEBUG_AI_MOVE + AAS_ClearShownDebugLines(); + AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); + AAS_ShowReachability(&reach); +#endif //DEBUG_AI_MOVE + // +#ifdef DEBUG + //botimport.Print(PRT_MESSAGE, "client %d: ", ms->client); + //AAS_PrintTravelType(reach.traveltype); + //botimport.Print(PRT_MESSAGE, "\n"); +#endif //DEBUG + switch(reach.traveltype & TRAVELTYPE_MASK) + { + case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break; + case TRAVEL_CROUCH: *result = BotTravel_Crouch(ms, &reach); break; + case TRAVEL_BARRIERJUMP: *result = BotTravel_BarrierJump(ms, &reach); break; + case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break; + case TRAVEL_WALKOFFLEDGE: *result = BotTravel_WalkOffLedge(ms, &reach); break; + case TRAVEL_JUMP: *result = BotTravel_Jump(ms, &reach); break; + case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break; + case TRAVEL_WATERJUMP: *result = BotTravel_WaterJump(ms, &reach); break; + case TRAVEL_TELEPORT: *result = BotTravel_Teleport(ms, &reach); break; + case TRAVEL_ELEVATOR: *result = BotTravel_Elevator(ms, &reach); break; + case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break; + case TRAVEL_ROCKETJUMP: *result = BotTravel_RocketJump(ms, &reach); break; + case TRAVEL_BFGJUMP: *result = BotTravel_BFGJump(ms, &reach); break; + case TRAVEL_JUMPPAD: *result = BotTravel_JumpPad(ms, &reach); break; + case TRAVEL_FUNCBOB: *result = BotTravel_FuncBobbing(ms, &reach); break; + default: + { + botimport.Print(PRT_FATAL, "travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK)); + break; + } //end case + } //end switch + result->traveltype = reach.traveltype; + result->flags |= resultflags; + } //end if + else + { + result->failure = qtrue; + result->flags |= resultflags; + Com_Memset(&reach, 0, sizeof(aas_reachability_t)); + } //end else +#ifdef DEBUG + if (bot_developer) + { + if (result->failure) + { + botimport.Print(PRT_MESSAGE, "client %d: movement failure in ", ms->client); + AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); + botimport.Print(PRT_MESSAGE, "\n"); + } //end if + } //end if +#endif //DEBUG + } //end if + else + { + int i, numareas, areas[16]; + vec3_t end; + + //special handling of jump pads when the bot uses a jump pad without knowing it + foundjumppad = qfalse; + VectorMA(ms->origin, -2 * ms->thinktime, ms->velocity, end); + numareas = AAS_TraceAreas(ms->origin, end, areas, NULL, 16); + for (i = numareas-1; i >= 0; i--) + { + if (AAS_AreaJumpPad(areas[i])) + { + //botimport.Print(PRT_MESSAGE, "client %d used a jumppad without knowing, area %d\n", ms->client, areas[i]); + foundjumppad = qtrue; + lastreachnum = BotGetReachabilityToGoal(end, areas[i], + ms->lastgoalareanum, ms->lastareanum, + ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries, + goal, travelflags, TFL_JUMPPAD, ms->avoidspots, ms->numavoidspots, NULL); + if (lastreachnum) + { + ms->lastreachnum = lastreachnum; + ms->lastareanum = areas[i]; + //botimport.Print(PRT_MESSAGE, "found jumppad reachability\n"); + break; + } //end if + else + { + for (lastreachnum = AAS_NextAreaReachability(areas[i], 0); lastreachnum; + lastreachnum = AAS_NextAreaReachability(areas[i], lastreachnum)) + { + //get the reachability from the number + AAS_ReachabilityFromNum(lastreachnum, &reach); + if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) + { + ms->lastreachnum = lastreachnum; + ms->lastareanum = areas[i]; + //botimport.Print(PRT_MESSAGE, "found jumppad reachability hard!!\n"); + break; + } //end if + } //end for + if (lastreachnum) break; + } //end else + } //end if + } //end for + if (bot_developer) + { + //if a jumppad is found with the trace but no reachability is found + if (foundjumppad && !ms->lastreachnum) + { + botimport.Print(PRT_MESSAGE, "client %d didn't find jumppad reachability\n", ms->client); + } //end if + } //end if + // + if (ms->lastreachnum) + { + //botimport.Print(PRT_MESSAGE, "%s: NOT onground, swimming or against ladder\n", ClientName(ms->entitynum-1)); + AAS_ReachabilityFromNum(ms->lastreachnum, &reach); + result->traveltype = reach.traveltype; +#ifdef DEBUG + //botimport.Print(PRT_MESSAGE, "client %d finish: ", ms->client); + //AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); + //botimport.Print(PRT_MESSAGE, "\n"); +#endif //DEBUG + // + switch(reach.traveltype & TRAVELTYPE_MASK) + { + case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break;//BotFinishTravel_Walk(ms, &reach); break; + case TRAVEL_CROUCH: /*do nothing*/ break; + case TRAVEL_BARRIERJUMP: *result = BotFinishTravel_BarrierJump(ms, &reach); break; + case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break; + case TRAVEL_WALKOFFLEDGE: *result = BotFinishTravel_WalkOffLedge(ms, &reach); break; + case TRAVEL_JUMP: *result = BotFinishTravel_Jump(ms, &reach); break; + case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break; + case TRAVEL_WATERJUMP: *result = BotFinishTravel_WaterJump(ms, &reach); break; + case TRAVEL_TELEPORT: /*do nothing*/ break; + case TRAVEL_ELEVATOR: *result = BotFinishTravel_Elevator(ms, &reach); break; + case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break; + case TRAVEL_ROCKETJUMP: + case TRAVEL_BFGJUMP: *result = BotFinishTravel_WeaponJump(ms, &reach); break; + case TRAVEL_JUMPPAD: *result = BotFinishTravel_JumpPad(ms, &reach); break; + case TRAVEL_FUNCBOB: *result = BotFinishTravel_FuncBobbing(ms, &reach); break; + default: + { + botimport.Print(PRT_FATAL, "(last) travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK)); + break; + } //end case + } //end switch + result->traveltype = reach.traveltype; +#ifdef DEBUG + if (bot_developer) + { + if (result->failure) + { + botimport.Print(PRT_MESSAGE, "client %d: movement failure in finish ", ms->client); + AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); + botimport.Print(PRT_MESSAGE, "\n"); + } //end if + } //end if +#endif //DEBUG + } //end if + } //end else + //FIXME: is it right to do this here? + if (result->blocked) ms->reachability_time -= 10 * ms->thinktime; + //copy the last origin + VectorCopy(ms->origin, ms->lastorigin); + //return the movement result + return; +} //end of the function BotMoveToGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetAvoidReach(int movestate) +{ + bot_movestate_t *ms; + + ms = BotMoveStateFromHandle(movestate); + if (!ms) return; + Com_Memset(ms->avoidreach, 0, MAX_AVOIDREACH * sizeof(int)); + Com_Memset(ms->avoidreachtimes, 0, MAX_AVOIDREACH * sizeof(float)); + Com_Memset(ms->avoidreachtries, 0, MAX_AVOIDREACH * sizeof(int)); +} //end of the function BotResetAvoidReach +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetLastAvoidReach(int movestate) +{ + int i, latest; + float latesttime; + bot_movestate_t *ms; + + ms = BotMoveStateFromHandle(movestate); + if (!ms) return; + latesttime = 0; + latest = 0; + for (i = 0; i < MAX_AVOIDREACH; i++) + { + if (ms->avoidreachtimes[i] > latesttime) + { + latesttime = ms->avoidreachtimes[i]; + latest = i; + } //end if + } //end for + if (latesttime) + { + ms->avoidreachtimes[latest] = 0; + if (ms->avoidreachtries[i] > 0) ms->avoidreachtries[latest]--; + } //end if +} //end of the function BotResetLastAvoidReach +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetMoveState(int movestate) +{ + bot_movestate_t *ms; + + ms = BotMoveStateFromHandle(movestate); + if (!ms) return; + Com_Memset(ms, 0, sizeof(bot_movestate_t)); +} //end of the function BotResetMoveState +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotSetupMoveAI(void) +{ + BotSetBrushModelTypes(); + sv_maxstep = LibVar("sv_step", "18"); + sv_maxbarrier = LibVar("sv_maxbarrier", "32"); + sv_gravity = LibVar("sv_gravity", "800"); + weapindex_rocketlauncher = LibVar("weapindex_rocketlauncher", "5"); + weapindex_bfg10k = LibVar("weapindex_bfg10k", "9"); + weapindex_grapple = LibVar("weapindex_grapple", "10"); + entitytypemissile = LibVar("entitytypemissile", "3"); + offhandgrapple = LibVar("offhandgrapple", "0"); + cmd_grappleon = LibVar("cmd_grappleon", "grappleon"); + cmd_grappleoff = LibVar("cmd_grappleoff", "grappleoff"); + return BLERR_NOERROR; +} //end of the function BotSetupMoveAI +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotShutdownMoveAI(void) +{ + int i; + + for (i = 1; i <= MAX_CLIENTS; i++) + { + if (botmovestates[i]) + { + FreeMemory(botmovestates[i]); + botmovestates[i] = NULL; + } //end if + } //end for +} //end of the function BotShutdownMoveAI + + diff --git a/reaction/engine/code/botlib/be_ai_move.h b/reaction/engine/code/botlib/be_ai_move.h new file mode 100644 index 00000000..a32d9397 --- /dev/null +++ b/reaction/engine/code/botlib/be_ai_move.h @@ -0,0 +1,142 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: be_ai_move.h + * + * desc: movement AI + * + * $Archive: /source/code/botlib/be_ai_move.h $ + * + *****************************************************************************/ + +//movement types +#define MOVE_WALK 1 +#define MOVE_CROUCH 2 +#define MOVE_JUMP 4 +#define MOVE_GRAPPLE 8 +#define MOVE_ROCKETJUMP 16 +#define MOVE_BFGJUMP 32 +//move flags +#define MFL_BARRIERJUMP 1 //bot is performing a barrier jump +#define MFL_ONGROUND 2 //bot is in the ground +#define MFL_SWIMMING 4 //bot is swimming +#define MFL_AGAINSTLADDER 8 //bot is against a ladder +#define MFL_WATERJUMP 16 //bot is waterjumping +#define MFL_TELEPORTED 32 //bot is being teleported +#define MFL_GRAPPLEPULL 64 //bot is being pulled by the grapple +#define MFL_ACTIVEGRAPPLE 128 //bot is using the grapple hook +#define MFL_GRAPPLERESET 256 //bot has reset the grapple +#define MFL_WALK 512 //bot should walk slowly +// move result flags +#define MOVERESULT_MOVEMENTVIEW 1 //bot uses view for movement +#define MOVERESULT_SWIMVIEW 2 //bot uses view for swimming +#define MOVERESULT_WAITING 4 //bot is waiting for something +#define MOVERESULT_MOVEMENTVIEWSET 8 //bot has set the view in movement code +#define MOVERESULT_MOVEMENTWEAPON 16 //bot uses weapon for movement +#define MOVERESULT_ONTOPOFOBSTACLE 32 //bot is ontop of obstacle +#define MOVERESULT_ONTOPOF_FUNCBOB 64 //bot is ontop of a func_bobbing +#define MOVERESULT_ONTOPOF_ELEVATOR 128 //bot is ontop of an elevator (func_plat) +#define MOVERESULT_BLOCKEDBYAVOIDSPOT 256 //bot is blocked by an avoid spot +// +#define MAX_AVOIDREACH 1 +#define MAX_AVOIDSPOTS 32 +// avoid spot types +#define AVOID_CLEAR 0 //clear all avoid spots +#define AVOID_ALWAYS 1 //avoid always +#define AVOID_DONTBLOCK 2 //never totally block +// restult types +#define RESULTTYPE_ELEVATORUP 1 //elevator is up +#define RESULTTYPE_WAITFORFUNCBOBBING 2 //waiting for func bobbing to arrive +#define RESULTTYPE_BADGRAPPLEPATH 4 //grapple path is obstructed +#define RESULTTYPE_INSOLIDAREA 8 //stuck in solid area, this is bad + +//structure used to initialize the movement state +//the or_moveflags MFL_ONGROUND, MFL_TELEPORTED and MFL_WATERJUMP come from the playerstate +typedef struct bot_initmove_s +{ + vec3_t origin; //origin of the bot + vec3_t velocity; //velocity of the bot + vec3_t viewoffset; //view offset + int entitynum; //entity number of the bot + int client; //client number of the bot + float thinktime; //time the bot thinks + int presencetype; //presencetype of the bot + vec3_t viewangles; //view angles of the bot + int or_moveflags; //values ored to the movement flags +} bot_initmove_t; + +//NOTE: the ideal_viewangles are only valid if MFL_MOVEMENTVIEW is set +typedef struct bot_moveresult_s +{ + int failure; //true if movement failed all together + int type; //failure or blocked type + int blocked; //true if blocked by an entity + int blockentity; //entity blocking the bot + int traveltype; //last executed travel type + int flags; //result flags + int weapon; //weapon used for movement + vec3_t movedir; //movement direction + vec3_t ideal_viewangles; //ideal viewangles for the movement +} bot_moveresult_t; + +#define bot_moveresult_t_cleared(x) bot_moveresult_t (x) = {0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, {0, 0, 0}} + +typedef struct bot_avoidspot_s +{ + vec3_t origin; + float radius; + int type; +} bot_avoidspot_t; + +//resets the whole move state +void BotResetMoveState(int movestate); +//moves the bot to the given goal +void BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, int travelflags); +//moves the bot in the specified direction using the specified type of movement +int BotMoveInDirection(int movestate, vec3_t dir, float speed, int type); +//reset avoid reachability +void BotResetAvoidReach(int movestate); +//resets the last avoid reachability +void BotResetLastAvoidReach(int movestate); +//returns a reachability area if the origin is in one +int BotReachabilityArea(vec3_t origin, int client); +//view target based on movement +int BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target); +//predict the position of a player based on movement towards a goal +int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target); +//returns the handle of a newly allocated movestate +int BotAllocMoveState(void); +//frees the movestate with the given handle +void BotFreeMoveState(int handle); +//initialize movement state before performing any movement +void BotInitMoveState(int handle, bot_initmove_t *initmove); +//add a spot to avoid (if type == AVOID_CLEAR all spots are removed) +void BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type); +//must be called every map change +void BotSetBrushModelTypes(void); +//setup movement AI +int BotSetupMoveAI(void); +//shutdown movement AI +void BotShutdownMoveAI(void); + diff --git a/reaction/engine/code/botlib/be_ai_weap.c b/reaction/engine/code/botlib/be_ai_weap.c new file mode 100644 index 00000000..0aab5e8c --- /dev/null +++ b/reaction/engine/code/botlib/be_ai_weap.c @@ -0,0 +1,543 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_weap.c + * + * desc: weapon AI + * + * $Archive: /MissionPack/code/botlib/be_ai_weap.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_libvar.h" +#include "l_log.h" +#include "l_memory.h" +#include "l_utils.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_ai_weight.h" //fuzzy weights +#include "be_ai_weap.h" + +//#define DEBUG_AI_WEAP + +//structure field offsets +#define WEAPON_OFS(x) (size_t)&(((weaponinfo_t *)0)->x) +#define PROJECTILE_OFS(x) (size_t)&(((projectileinfo_t *)0)->x) + +//weapon definition +static fielddef_t weaponinfo_fields[] = +{ +{"number", WEAPON_OFS(number), FT_INT}, //weapon number +{"name", WEAPON_OFS(name), FT_STRING}, //name of the weapon +{"level", WEAPON_OFS(level), FT_INT}, +{"model", WEAPON_OFS(model), FT_STRING}, //model of the weapon +{"weaponindex", WEAPON_OFS(weaponindex), FT_INT}, //index of weapon in inventory +{"flags", WEAPON_OFS(flags), FT_INT}, //special flags +{"projectile", WEAPON_OFS(projectile), FT_STRING}, //projectile used by the weapon +{"numprojectiles", WEAPON_OFS(numprojectiles), FT_INT}, //number of projectiles +{"hspread", WEAPON_OFS(hspread), FT_FLOAT}, //horizontal spread of projectiles (degrees from middle) +{"vspread", WEAPON_OFS(vspread), FT_FLOAT}, //vertical spread of projectiles (degrees from middle) +{"speed", WEAPON_OFS(speed), FT_FLOAT}, //speed of the projectile (0 = instant hit) +{"acceleration", WEAPON_OFS(acceleration), FT_FLOAT}, //"acceleration" * time (in seconds) + "speed" = projectile speed +{"recoil", WEAPON_OFS(recoil), FT_FLOAT|FT_ARRAY, 3}, //amount of recoil the player gets from the weapon +{"offset", WEAPON_OFS(offset), FT_FLOAT|FT_ARRAY, 3}, //projectile start offset relative to eye and view angles +{"angleoffset", WEAPON_OFS(angleoffset), FT_FLOAT|FT_ARRAY, 3},//offset of the shoot angles relative to the view angles +{"extrazvelocity", WEAPON_OFS(extrazvelocity), FT_FLOAT},//extra z velocity the projectile gets +{"ammoamount", WEAPON_OFS(ammoamount), FT_INT}, //ammo amount used per shot +{"ammoindex", WEAPON_OFS(ammoindex), FT_INT}, //index of ammo in inventory +{"activate", WEAPON_OFS(activate), FT_FLOAT}, //time it takes to select the weapon +{"reload", WEAPON_OFS(reload), FT_FLOAT}, //time it takes to reload the weapon +{"spinup", WEAPON_OFS(spinup), FT_FLOAT}, //time it takes before first shot +{"spindown", WEAPON_OFS(spindown), FT_FLOAT}, //time it takes before weapon stops firing +{NULL, 0, 0, 0} +}; + +//projectile definition +static fielddef_t projectileinfo_fields[] = +{ +{"name", PROJECTILE_OFS(name), FT_STRING}, //name of the projectile +{"model", WEAPON_OFS(model), FT_STRING}, //model of the projectile +{"flags", PROJECTILE_OFS(flags), FT_INT}, //special flags +{"gravity", PROJECTILE_OFS(gravity), FT_FLOAT}, //amount of gravity applied to the projectile [0,1] +{"damage", PROJECTILE_OFS(damage), FT_INT}, //damage of the projectile +{"radius", PROJECTILE_OFS(radius), FT_FLOAT}, //radius of damage +{"visdamage", PROJECTILE_OFS(visdamage), FT_INT}, //damage of the projectile to visible entities +{"damagetype", PROJECTILE_OFS(damagetype), FT_INT}, //type of damage (combination of the DAMAGETYPE_? flags) +{"healthinc", PROJECTILE_OFS(healthinc), FT_INT}, //health increase the owner gets +{"push", PROJECTILE_OFS(push), FT_FLOAT}, //amount a player is pushed away from the projectile impact +{"detonation", PROJECTILE_OFS(detonation), FT_FLOAT}, //time before projectile explodes after fire pressed +{"bounce", PROJECTILE_OFS(bounce), FT_FLOAT}, //amount the projectile bounces +{"bouncefric", PROJECTILE_OFS(bouncefric), FT_FLOAT}, //amount the bounce decreases per bounce +{"bouncestop", PROJECTILE_OFS(bouncestop), FT_FLOAT}, //minimum bounce value before bouncing stops +//recurive projectile definition?? +{NULL, 0, 0, 0} +}; + +static structdef_t weaponinfo_struct = +{ + sizeof(weaponinfo_t), weaponinfo_fields +}; +static structdef_t projectileinfo_struct = +{ + sizeof(projectileinfo_t), projectileinfo_fields +}; + +//weapon configuration: set of weapons with projectiles +typedef struct weaponconfig_s +{ + int numweapons; + int numprojectiles; + projectileinfo_t *projectileinfo; + weaponinfo_t *weaponinfo; +} weaponconfig_t; + +//the bot weapon state +typedef struct bot_weaponstate_s +{ + struct weightconfig_s *weaponweightconfig; //weapon weight configuration + int *weaponweightindex; //weapon weight index +} bot_weaponstate_t; + +static bot_weaponstate_t *botweaponstates[MAX_CLIENTS+1]; +static weaponconfig_t *weaponconfig; + +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +int BotValidWeaponNumber(int weaponnum) +{ + if (weaponnum <= 0 || weaponnum > weaponconfig->numweapons) + { + botimport.Print(PRT_ERROR, "weapon number out of range\n"); + return qfalse; + } //end if + return qtrue; +} //end of the function BotValidWeaponNumber +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +bot_weaponstate_t *BotWeaponStateFromHandle(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); + return NULL; + } //end if + if (!botweaponstates[handle]) + { + botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); + return NULL; + } //end if + return botweaponstates[handle]; +} //end of the function BotWeaponStateFromHandle +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef DEBUG_AI_WEAP +void DumpWeaponConfig(weaponconfig_t *wc) +{ + FILE *fp; + int i; + + fp = Log_FileStruct(); + if (!fp) return; + for (i = 0; i < wc->numprojectiles; i++) + { + WriteStructure(fp, &projectileinfo_struct, (char *) &wc->projectileinfo[i]); + Log_Flush(); + } //end for + for (i = 0; i < wc->numweapons; i++) + { + WriteStructure(fp, &weaponinfo_struct, (char *) &wc->weaponinfo[i]); + Log_Flush(); + } //end for +} //end of the function DumpWeaponConfig +#endif //DEBUG_AI_WEAP +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +weaponconfig_t *LoadWeaponConfig(char *filename) +{ + int max_weaponinfo, max_projectileinfo; + token_t token; + char path[MAX_PATH]; + int i, j; + source_t *source; + weaponconfig_t *wc; + weaponinfo_t weaponinfo; + + max_weaponinfo = (int) LibVarValue("max_weaponinfo", "32"); + if (max_weaponinfo < 0) + { + botimport.Print(PRT_ERROR, "max_weaponinfo = %d\n", max_weaponinfo); + max_weaponinfo = 32; + LibVarSet("max_weaponinfo", "32"); + } //end if + max_projectileinfo = (int) LibVarValue("max_projectileinfo", "32"); + if (max_projectileinfo < 0) + { + botimport.Print(PRT_ERROR, "max_projectileinfo = %d\n", max_projectileinfo); + max_projectileinfo = 32; + LibVarSet("max_projectileinfo", "32"); + } //end if + strncpy(path, filename, MAX_PATH); + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(path); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", path); + return NULL; + } //end if + //initialize weapon config + wc = (weaponconfig_t *) GetClearedHunkMemory(sizeof(weaponconfig_t) + + max_weaponinfo * sizeof(weaponinfo_t) + + max_projectileinfo * sizeof(projectileinfo_t)); + wc->weaponinfo = (weaponinfo_t *) ((char *) wc + sizeof(weaponconfig_t)); + wc->projectileinfo = (projectileinfo_t *) ((char *) wc->weaponinfo + + max_weaponinfo * sizeof(weaponinfo_t)); + wc->numweapons = max_weaponinfo; + wc->numprojectiles = 0; + //parse the source file + while(PC_ReadToken(source, &token)) + { + if (!strcmp(token.string, "weaponinfo")) + { + Com_Memset(&weaponinfo, 0, sizeof(weaponinfo_t)); + if (!ReadStructure(source, &weaponinfo_struct, (char *) &weaponinfo)) + { + FreeMemory(wc); + FreeSource(source); + return NULL; + } //end if + if (weaponinfo.number < 0 || weaponinfo.number >= max_weaponinfo) + { + botimport.Print(PRT_ERROR, "weapon info number %d out of range in %s\n", weaponinfo.number, path); + FreeMemory(wc); + FreeSource(source); + return NULL; + } //end if + Com_Memcpy(&wc->weaponinfo[weaponinfo.number], &weaponinfo, sizeof(weaponinfo_t)); + wc->weaponinfo[weaponinfo.number].valid = qtrue; + } //end if + else if (!strcmp(token.string, "projectileinfo")) + { + if (wc->numprojectiles >= max_projectileinfo) + { + botimport.Print(PRT_ERROR, "more than %d projectiles defined in %s\n", max_projectileinfo, path); + FreeMemory(wc); + FreeSource(source); + return NULL; + } //end if + Com_Memset(&wc->projectileinfo[wc->numprojectiles], 0, sizeof(projectileinfo_t)); + if (!ReadStructure(source, &projectileinfo_struct, (char *) &wc->projectileinfo[wc->numprojectiles])) + { + FreeMemory(wc); + FreeSource(source); + return NULL; + } //end if + wc->numprojectiles++; + } //end if + else + { + botimport.Print(PRT_ERROR, "unknown definition %s in %s\n", token.string, path); + FreeMemory(wc); + FreeSource(source); + return NULL; + } //end else + } //end while + FreeSource(source); + //fix up weapons + for (i = 0; i < wc->numweapons; i++) + { + if (!wc->weaponinfo[i].valid) continue; + if (!wc->weaponinfo[i].name[0]) + { + botimport.Print(PRT_ERROR, "weapon %d has no name in %s\n", i, path); + FreeMemory(wc); + return NULL; + } //end if + if (!wc->weaponinfo[i].projectile[0]) + { + botimport.Print(PRT_ERROR, "weapon %s has no projectile in %s\n", wc->weaponinfo[i].name, path); + FreeMemory(wc); + return NULL; + } //end if + //find the projectile info and copy it to the weapon info + for (j = 0; j < wc->numprojectiles; j++) + { + if (!strcmp(wc->projectileinfo[j].name, wc->weaponinfo[i].projectile)) + { + Com_Memcpy(&wc->weaponinfo[i].proj, &wc->projectileinfo[j], sizeof(projectileinfo_t)); + break; + } //end if + } //end for + if (j == wc->numprojectiles) + { + botimport.Print(PRT_ERROR, "weapon %s uses undefined projectile in %s\n", wc->weaponinfo[i].name, path); + FreeMemory(wc); + return NULL; + } //end if + } //end for + if (!wc->numweapons) botimport.Print(PRT_WARNING, "no weapon info loaded\n"); + botimport.Print(PRT_MESSAGE, "loaded %s\n", path); + return wc; +} //end of the function LoadWeaponConfig +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int *WeaponWeightIndex(weightconfig_t *wwc, weaponconfig_t *wc) +{ + int *index, i; + + //initialize item weight index + index = (int *) GetClearedMemory(sizeof(int) * wc->numweapons); + + for (i = 0; i < wc->numweapons; i++) + { + index[i] = FindFuzzyWeight(wwc, wc->weaponinfo[i].name); + } //end for + return index; +} //end of the function WeaponWeightIndex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFreeWeaponWeights(int weaponstate) +{ + bot_weaponstate_t *ws; + + ws = BotWeaponStateFromHandle(weaponstate); + if (!ws) return; + if (ws->weaponweightconfig) FreeWeightConfig(ws->weaponweightconfig); + if (ws->weaponweightindex) FreeMemory(ws->weaponweightindex); +} //end of the function BotFreeWeaponWeights +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadWeaponWeights(int weaponstate, char *filename) +{ + bot_weaponstate_t *ws; + + ws = BotWeaponStateFromHandle(weaponstate); + if (!ws) return BLERR_CANNOTLOADWEAPONWEIGHTS; + BotFreeWeaponWeights(weaponstate); + // + ws->weaponweightconfig = ReadWeightConfig(filename); + if (!ws->weaponweightconfig) + { + botimport.Print(PRT_FATAL, "couldn't load weapon config %s\n", filename); + return BLERR_CANNOTLOADWEAPONWEIGHTS; + } //end if + if (!weaponconfig) return BLERR_CANNOTLOADWEAPONCONFIG; + ws->weaponweightindex = WeaponWeightIndex(ws->weaponweightconfig, weaponconfig); + return BLERR_NOERROR; +} //end of the function BotLoadWeaponWeights +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotGetWeaponInfo(int weaponstate, int weapon, weaponinfo_t *weaponinfo) +{ + bot_weaponstate_t *ws; + + if (!BotValidWeaponNumber(weapon)) return; + ws = BotWeaponStateFromHandle(weaponstate); + if (!ws) return; + if (!weaponconfig) return; + Com_Memcpy(weaponinfo, &weaponconfig->weaponinfo[weapon], sizeof(weaponinfo_t)); +} //end of the function BotGetWeaponInfo +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotChooseBestFightWeapon(int weaponstate, int *inventory) +{ + int i, index, bestweapon; + float weight, bestweight; + weaponconfig_t *wc; + bot_weaponstate_t *ws; + + ws = BotWeaponStateFromHandle(weaponstate); + if (!ws) return 0; + wc = weaponconfig; + if (!weaponconfig) return 0; + + //if the bot has no weapon weight configuration + if (!ws->weaponweightconfig) return 0; + + bestweight = 0; + bestweapon = 0; + for (i = 0; i < wc->numweapons; i++) + { + if (!wc->weaponinfo[i].valid) continue; + index = ws->weaponweightindex[i]; + if (index < 0) continue; + weight = FuzzyWeight(inventory, ws->weaponweightconfig, index); + if (weight > bestweight) + { + bestweight = weight; + bestweapon = i; + } //end if + } //end for + return bestweapon; +} //end of the function BotChooseBestFightWeapon +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetWeaponState(int weaponstate) +{ + struct weightconfig_s *weaponweightconfig; + int *weaponweightindex; + bot_weaponstate_t *ws; + + ws = BotWeaponStateFromHandle(weaponstate); + if (!ws) return; + weaponweightconfig = ws->weaponweightconfig; + weaponweightindex = ws->weaponweightindex; + + //Com_Memset(ws, 0, sizeof(bot_weaponstate_t)); + ws->weaponweightconfig = weaponweightconfig; + ws->weaponweightindex = weaponweightindex; +} //end of the function BotResetWeaponState +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +int BotAllocWeaponState(void) +{ + int i; + + for (i = 1; i <= MAX_CLIENTS; i++) + { + if (!botweaponstates[i]) + { + botweaponstates[i] = GetClearedMemory(sizeof(bot_weaponstate_t)); + return i; + } //end if + } //end for + return 0; +} //end of the function BotAllocWeaponState +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeWeaponState(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); + return; + } //end if + if (!botweaponstates[handle]) + { + botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); + return; + } //end if + BotFreeWeaponWeights(handle); + FreeMemory(botweaponstates[handle]); + botweaponstates[handle] = NULL; +} //end of the function BotFreeWeaponState +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotSetupWeaponAI(void) +{ + char *file; + + file = LibVarString("weaponconfig", "weapons.c"); + weaponconfig = LoadWeaponConfig(file); + if (!weaponconfig) + { + botimport.Print(PRT_FATAL, "couldn't load the weapon config\n"); + return BLERR_CANNOTLOADWEAPONCONFIG; + } //end if + +#ifdef DEBUG_AI_WEAP + DumpWeaponConfig(weaponconfig); +#endif //DEBUG_AI_WEAP + // + return BLERR_NOERROR; +} //end of the function BotSetupWeaponAI +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotShutdownWeaponAI(void) +{ + int i; + + if (weaponconfig) FreeMemory(weaponconfig); + weaponconfig = NULL; + + for (i = 1; i <= MAX_CLIENTS; i++) + { + if (botweaponstates[i]) + { + BotFreeWeaponState(i); + } //end if + } //end for +} //end of the function BotShutdownWeaponAI + diff --git a/reaction/engine/code/botlib/be_ai_weap.h b/reaction/engine/code/botlib/be_ai_weap.h new file mode 100644 index 00000000..59067fb0 --- /dev/null +++ b/reaction/engine/code/botlib/be_ai_weap.h @@ -0,0 +1,104 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: be_ai_weap.h + * + * desc: weapon AI + * + * $Archive: /source/code/botlib/be_ai_weap.h $ + * + *****************************************************************************/ + +//projectile flags +#define PFL_WINDOWDAMAGE 1 //projectile damages through window +#define PFL_RETURN 2 //set when projectile returns to owner +//weapon flags +#define WFL_FIRERELEASED 1 //set when projectile is fired with key-up event +//damage types +#define DAMAGETYPE_IMPACT 1 //damage on impact +#define DAMAGETYPE_RADIAL 2 //radial damage +#define DAMAGETYPE_VISIBLE 4 //damage to all entities visible to the projectile + +typedef struct projectileinfo_s +{ + char name[MAX_STRINGFIELD]; + char model[MAX_STRINGFIELD]; + int flags; + float gravity; + int damage; + float radius; + int visdamage; + int damagetype; + int healthinc; + float push; + float detonation; + float bounce; + float bouncefric; + float bouncestop; +} projectileinfo_t; + +typedef struct weaponinfo_s +{ + int valid; //true if the weapon info is valid + int number; //number of the weapon + char name[MAX_STRINGFIELD]; + char model[MAX_STRINGFIELD]; + int level; + int weaponindex; + int flags; + char projectile[MAX_STRINGFIELD]; + int numprojectiles; + float hspread; + float vspread; + float speed; + float acceleration; + vec3_t recoil; + vec3_t offset; + vec3_t angleoffset; + float extrazvelocity; + int ammoamount; + int ammoindex; + float activate; + float reload; + float spinup; + float spindown; + projectileinfo_t proj; //pointer to the used projectile +} weaponinfo_t; + +//setup the weapon AI +int BotSetupWeaponAI(void); +//shut down the weapon AI +void BotShutdownWeaponAI(void); +//returns the best weapon to fight with +int BotChooseBestFightWeapon(int weaponstate, int *inventory); +//returns the information of the current weapon +void BotGetWeaponInfo(int weaponstate, int weapon, weaponinfo_t *weaponinfo); +//loads the weapon weights +int BotLoadWeaponWeights(int weaponstate, char *filename); +//returns a handle to a newly allocated weapon state +int BotAllocWeaponState(void); +//frees the weapon state +void BotFreeWeaponState(int weaponstate); +//resets the whole weapon state +void BotResetWeaponState(int weaponstate); diff --git a/reaction/engine/code/botlib/be_ai_weight.c b/reaction/engine/code/botlib/be_ai_weight.c new file mode 100644 index 00000000..8d48ee62 --- /dev/null +++ b/reaction/engine/code/botlib/be_ai_weight.c @@ -0,0 +1,918 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_weight.c + * + * desc: fuzzy logic + * + * $Archive: /MissionPack/code/botlib/be_ai_weight.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_log.h" +#include "l_utils.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_libvar.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_ai_weight.h" + +#define MAX_INVENTORYVALUE 999999 +#define EVALUATERECURSIVELY + +#define MAX_WEIGHT_FILES 128 +weightconfig_t *weightFileList[MAX_WEIGHT_FILES]; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int ReadValue(source_t *source, float *value) +{ + token_t token; + + if (!PC_ExpectAnyToken(source, &token)) return qfalse; + if (!strcmp(token.string, "-")) + { + SourceWarning(source, "negative value set to zero\n"); + if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) return qfalse; + } //end if + if (token.type != TT_NUMBER) + { + SourceError(source, "invalid return value %s\n", token.string); + return qfalse; + } //end if + *value = token.floatvalue; + return qtrue; +} //end of the function ReadValue +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int ReadFuzzyWeight(source_t *source, fuzzyseperator_t *fs) +{ + if (PC_CheckTokenString(source, "balance")) + { + fs->type = WT_BALANCE; + if (!PC_ExpectTokenString(source, "(")) return qfalse; + if (!ReadValue(source, &fs->weight)) return qfalse; + if (!PC_ExpectTokenString(source, ",")) return qfalse; + if (!ReadValue(source, &fs->minweight)) return qfalse; + if (!PC_ExpectTokenString(source, ",")) return qfalse; + if (!ReadValue(source, &fs->maxweight)) return qfalse; + if (!PC_ExpectTokenString(source, ")")) return qfalse; + } //end if + else + { + fs->type = 0; + if (!ReadValue(source, &fs->weight)) return qfalse; + fs->minweight = fs->weight; + fs->maxweight = fs->weight; + } //end if + if (!PC_ExpectTokenString(source, ";")) return qfalse; + return qtrue; +} //end of the function ReadFuzzyWeight +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeFuzzySeperators_r(fuzzyseperator_t *fs) +{ + if (!fs) return; + if (fs->child) FreeFuzzySeperators_r(fs->child); + if (fs->next) FreeFuzzySeperators_r(fs->next); + FreeMemory(fs); +} //end of the function FreeFuzzySeperators +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeWeightConfig2(weightconfig_t *config) +{ + int i; + + for (i = 0; i < config->numweights; i++) + { + FreeFuzzySeperators_r(config->weights[i].firstseperator); + if (config->weights[i].name) FreeMemory(config->weights[i].name); + } //end for + FreeMemory(config); +} //end of the function FreeWeightConfig2 +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeWeightConfig(weightconfig_t *config) +{ + if (!LibVarGetValue("bot_reloadcharacters")) return; + FreeWeightConfig2(config); +} //end of the function FreeWeightConfig +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +fuzzyseperator_t *ReadFuzzySeperators_r(source_t *source) +{ + int newindent, index, def, founddefault; + token_t token; + fuzzyseperator_t *fs, *lastfs, *firstfs; + + founddefault = qfalse; + firstfs = NULL; + lastfs = NULL; + if (!PC_ExpectTokenString(source, "(")) return NULL; + if (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) return NULL; + index = token.intvalue; + if (!PC_ExpectTokenString(source, ")")) return NULL; + if (!PC_ExpectTokenString(source, "{")) return NULL; + if (!PC_ExpectAnyToken(source, &token)) return NULL; + do + { + def = !strcmp(token.string, "default"); + if (def || !strcmp(token.string, "case")) + { + fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t)); + fs->index = index; + if (lastfs) lastfs->next = fs; + else firstfs = fs; + lastfs = fs; + if (def) + { + if (founddefault) + { + SourceError(source, "switch already has a default\n"); + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + fs->value = MAX_INVENTORYVALUE; + founddefault = qtrue; + } //end if + else + { + if (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) + { + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + fs->value = token.intvalue; + } //end else + if (!PC_ExpectTokenString(source, ":") || !PC_ExpectAnyToken(source, &token)) + { + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + newindent = qfalse; + if (!strcmp(token.string, "{")) + { + newindent = qtrue; + if (!PC_ExpectAnyToken(source, &token)) + { + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + } //end if + if (!strcmp(token.string, "return")) + { + if (!ReadFuzzyWeight(source, fs)) + { + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + } //end if + else if (!strcmp(token.string, "switch")) + { + fs->child = ReadFuzzySeperators_r(source); + if (!fs->child) + { + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + } //end else if + else + { + SourceError(source, "invalid name %s\n", token.string); + return NULL; + } //end else + if (newindent) + { + if (!PC_ExpectTokenString(source, "}")) + { + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + } //end if + } //end if + else + { + FreeFuzzySeperators_r(firstfs); + SourceError(source, "invalid name %s\n", token.string); + return NULL; + } //end else + if (!PC_ExpectAnyToken(source, &token)) + { + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + } while(strcmp(token.string, "}")); + // + if (!founddefault) + { + SourceWarning(source, "switch without default\n"); + fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t)); + fs->index = index; + fs->value = MAX_INVENTORYVALUE; + fs->weight = 0; + fs->next = NULL; + fs->child = NULL; + if (lastfs) lastfs->next = fs; + else firstfs = fs; + lastfs = fs; + } //end if + // + return firstfs; +} //end of the function ReadFuzzySeperators_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +weightconfig_t *ReadWeightConfig(char *filename) +{ + int newindent, avail = 0, n; + token_t token; + source_t *source; + fuzzyseperator_t *fs; + weightconfig_t *config = NULL; +#ifdef DEBUG + int starttime; + + starttime = Sys_MilliSeconds(); +#endif //DEBUG + + if (!LibVarGetValue("bot_reloadcharacters")) + { + avail = -1; + for( n = 0; n < MAX_WEIGHT_FILES; n++ ) + { + config = weightFileList[n]; + if( !config ) + { + if( avail == -1 ) + { + avail = n; + } //end if + continue; + } //end if + if( strcmp( filename, config->filename ) == 0 ) + { + //botimport.Print( PRT_MESSAGE, "retained %s\n", filename ); + return config; + } //end if + } //end for + + if( avail == -1 ) + { + botimport.Print( PRT_ERROR, "weightFileList was full trying to load %s\n", filename ); + return NULL; + } //end if + } //end if + + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(filename); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); + return NULL; + } //end if + // + config = (weightconfig_t *) GetClearedMemory(sizeof(weightconfig_t)); + config->numweights = 0; + Q_strncpyz( config->filename, filename, sizeof(config->filename) ); + //parse the item config file + while(PC_ReadToken(source, &token)) + { + if (!strcmp(token.string, "weight")) + { + if (config->numweights >= MAX_WEIGHTS) + { + SourceWarning(source, "too many fuzzy weights\n"); + break; + } //end if + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) + { + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end if + StripDoubleQuotes(token.string); + config->weights[config->numweights].name = (char *) GetClearedMemory(strlen(token.string) + 1); + strcpy(config->weights[config->numweights].name, token.string); + if (!PC_ExpectAnyToken(source, &token)) + { + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end if + newindent = qfalse; + if (!strcmp(token.string, "{")) + { + newindent = qtrue; + if (!PC_ExpectAnyToken(source, &token)) + { + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end if + } //end if + if (!strcmp(token.string, "switch")) + { + fs = ReadFuzzySeperators_r(source); + if (!fs) + { + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end if + config->weights[config->numweights].firstseperator = fs; + } //end if + else if (!strcmp(token.string, "return")) + { + fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t)); + fs->index = 0; + fs->value = MAX_INVENTORYVALUE; + fs->next = NULL; + fs->child = NULL; + if (!ReadFuzzyWeight(source, fs)) + { + FreeMemory(fs); + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end if + config->weights[config->numweights].firstseperator = fs; + } //end else if + else + { + SourceError(source, "invalid name %s\n", token.string); + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end else + if (newindent) + { + if (!PC_ExpectTokenString(source, "}")) + { + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end if + } //end if + config->numweights++; + } //end if + else + { + SourceError(source, "invalid name %s\n", token.string); + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end else + } //end while + //free the source at the end of a pass + FreeSource(source); + //if the file was located in a pak file + botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); +#ifdef DEBUG + if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "weights loaded in %d msec\n", Sys_MilliSeconds() - starttime); + } //end if +#endif //DEBUG + // + if (!LibVarGetValue("bot_reloadcharacters")) + { + weightFileList[avail] = config; + } //end if + // + return config; +} //end of the function ReadWeightConfig +#if 0 +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WriteFuzzyWeight(FILE *fp, fuzzyseperator_t *fs) +{ + if (fs->type == WT_BALANCE) + { + if (fprintf(fp, " return balance(") < 0) return qfalse; + if (!WriteFloat(fp, fs->weight)) return qfalse; + if (fprintf(fp, ",") < 0) return qfalse; + if (!WriteFloat(fp, fs->minweight)) return qfalse; + if (fprintf(fp, ",") < 0) return qfalse; + if (!WriteFloat(fp, fs->maxweight)) return qfalse; + if (fprintf(fp, ");\n") < 0) return qfalse; + } //end if + else + { + if (fprintf(fp, " return ") < 0) return qfalse; + if (!WriteFloat(fp, fs->weight)) return qfalse; + if (fprintf(fp, ";\n") < 0) return qfalse; + } //end else + return qtrue; +} //end of the function WriteFuzzyWeight +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WriteFuzzySeperators_r(FILE *fp, fuzzyseperator_t *fs, int indent) +{ + if (!WriteIndent(fp, indent)) return qfalse; + if (fprintf(fp, "switch(%d)\n", fs->index) < 0) return qfalse; + if (!WriteIndent(fp, indent)) return qfalse; + if (fprintf(fp, "{\n") < 0) return qfalse; + indent++; + do + { + if (!WriteIndent(fp, indent)) return qfalse; + if (fs->next) + { + if (fprintf(fp, "case %d:", fs->value) < 0) return qfalse; + } //end if + else + { + if (fprintf(fp, "default:") < 0) return qfalse; + } //end else + if (fs->child) + { + if (fprintf(fp, "\n") < 0) return qfalse; + if (!WriteIndent(fp, indent)) return qfalse; + if (fprintf(fp, "{\n") < 0) return qfalse; + if (!WriteFuzzySeperators_r(fp, fs->child, indent + 1)) return qfalse; + if (!WriteIndent(fp, indent)) return qfalse; + if (fs->next) + { + if (fprintf(fp, "} //end case\n") < 0) return qfalse; + } //end if + else + { + if (fprintf(fp, "} //end default\n") < 0) return qfalse; + } //end else + } //end if + else + { + if (!WriteFuzzyWeight(fp, fs)) return qfalse; + } //end else + fs = fs->next; + } while(fs); + indent--; + if (!WriteIndent(fp, indent)) return qfalse; + if (fprintf(fp, "} //end switch\n") < 0) return qfalse; + return qtrue; +} //end of the function WriteItemFuzzyWeights_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WriteWeightConfig(char *filename, weightconfig_t *config) +{ + int i; + FILE *fp; + weight_t *ifw; + + fp = fopen(filename, "wb"); + if (!fp) return qfalse; + + for (i = 0; i < config->numweights; i++) + { + ifw = &config->weights[i]; + if (fprintf(fp, "\nweight \"%s\"\n", ifw->name) < 0) return qfalse; + if (fprintf(fp, "{\n") < 0) return qfalse; + if (ifw->firstseperator->index > 0) + { + if (!WriteFuzzySeperators_r(fp, ifw->firstseperator, 1)) return qfalse; + } //end if + else + { + if (!WriteIndent(fp, 1)) return qfalse; + if (!WriteFuzzyWeight(fp, ifw->firstseperator)) return qfalse; + } //end else + if (fprintf(fp, "} //end weight\n") < 0) return qfalse; + } //end for + fclose(fp); + return qtrue; +} //end of the function WriteWeightConfig +#endif +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int FindFuzzyWeight(weightconfig_t *wc, char *name) +{ + int i; + + for (i = 0; i < wc->numweights; i++) + { + if (!strcmp(wc->weights[i].name, name)) + { + return i; + } //end if + } //end if + return -1; +} //end of the function FindFuzzyWeight +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float FuzzyWeight_r(int *inventory, fuzzyseperator_t *fs) +{ + float scale, w1, w2; + + if (inventory[fs->index] < fs->value) + { + if (fs->child) return FuzzyWeight_r(inventory, fs->child); + else return fs->weight; + } //end if + else if (fs->next) + { + if (inventory[fs->index] < fs->next->value) + { + //first weight + if (fs->child) w1 = FuzzyWeight_r(inventory, fs->child); + else w1 = fs->weight; + //second weight + if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child); + else w2 = fs->next->weight; + //the scale factor + if(fs->next->value == MAX_INVENTORYVALUE) // is fs->next the default case? + return w2; // can't interpolate, return default weight + else + scale = (float) (inventory[fs->index] - fs->value) / (fs->next->value - fs->value); + //scale between the two weights + return (1 - scale) * w1 + scale * w2; + } //end if + return FuzzyWeight_r(inventory, fs->next); + } //end else if + return fs->weight; +} //end of the function FuzzyWeight_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float FuzzyWeightUndecided_r(int *inventory, fuzzyseperator_t *fs) +{ + float scale, w1, w2; + + if (inventory[fs->index] < fs->value) + { + if (fs->child) return FuzzyWeightUndecided_r(inventory, fs->child); + else return fs->minweight + random() * (fs->maxweight - fs->minweight); + } //end if + else if (fs->next) + { + if (inventory[fs->index] < fs->next->value) + { + //first weight + if (fs->child) w1 = FuzzyWeightUndecided_r(inventory, fs->child); + else w1 = fs->minweight + random() * (fs->maxweight - fs->minweight); + //second weight + if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child); + else w2 = fs->next->minweight + random() * (fs->next->maxweight - fs->next->minweight); + //the scale factor + if(fs->next->value == MAX_INVENTORYVALUE) // is fs->next the default case? + return w2; // can't interpolate, return default weight + else + scale = (float) (inventory[fs->index] - fs->value) / (fs->next->value - fs->value); + //scale between the two weights + return (1 - scale) * w1 + scale * w2; + } //end if + return FuzzyWeightUndecided_r(inventory, fs->next); + } //end else if + return fs->weight; +} //end of the function FuzzyWeightUndecided_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum) +{ +#ifdef EVALUATERECURSIVELY + return FuzzyWeight_r(inventory, wc->weights[weightnum].firstseperator); +#else + fuzzyseperator_t *s; + + s = wc->weights[weightnum].firstseperator; + if (!s) return 0; + while(1) + { + if (inventory[s->index] < s->value) + { + if (s->child) s = s->child; + else return s->weight; + } //end if + else + { + if (s->next) s = s->next; + else return s->weight; + } //end else + } //end if + return 0; +#endif +} //end of the function FuzzyWeight +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum) +{ +#ifdef EVALUATERECURSIVELY + return FuzzyWeightUndecided_r(inventory, wc->weights[weightnum].firstseperator); +#else + fuzzyseperator_t *s; + + s = wc->weights[weightnum].firstseperator; + if (!s) return 0; + while(1) + { + if (inventory[s->index] < s->value) + { + if (s->child) s = s->child; + else return s->minweight + random() * (s->maxweight - s->minweight); + } //end if + else + { + if (s->next) s = s->next; + else return s->minweight + random() * (s->maxweight - s->minweight); + } //end else + } //end if + return 0; +#endif +} //end of the function FuzzyWeightUndecided +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EvolveFuzzySeperator_r(fuzzyseperator_t *fs) +{ + if (fs->child) + { + EvolveFuzzySeperator_r(fs->child); + } //end if + else if (fs->type == WT_BALANCE) + { + //every once in a while an evolution leap occurs, mutation + if (random() < 0.01) fs->weight += crandom() * (fs->maxweight - fs->minweight); + else fs->weight += crandom() * (fs->maxweight - fs->minweight) * 0.5; + //modify bounds if necesary because of mutation + if (fs->weight < fs->minweight) fs->minweight = fs->weight; + else if (fs->weight > fs->maxweight) fs->maxweight = fs->weight; + } //end else if + if (fs->next) EvolveFuzzySeperator_r(fs->next); +} //end of the function EvolveFuzzySeperator_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EvolveWeightConfig(weightconfig_t *config) +{ + int i; + + for (i = 0; i < config->numweights; i++) + { + EvolveFuzzySeperator_r(config->weights[i].firstseperator); + } //end for +} //end of the function EvolveWeightConfig +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ScaleFuzzySeperator_r(fuzzyseperator_t *fs, float scale) +{ + if (fs->child) + { + ScaleFuzzySeperator_r(fs->child, scale); + } //end if + else if (fs->type == WT_BALANCE) + { + // + fs->weight = (float) (fs->maxweight + fs->minweight) * scale; + //get the weight between bounds + if (fs->weight < fs->minweight) fs->weight = fs->minweight; + else if (fs->weight > fs->maxweight) fs->weight = fs->maxweight; + } //end else if + if (fs->next) ScaleFuzzySeperator_r(fs->next, scale); +} //end of the function ScaleFuzzySeperator_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ScaleWeight(weightconfig_t *config, char *name, float scale) +{ + int i; + + if (scale < 0) scale = 0; + else if (scale > 1) scale = 1; + for (i = 0; i < config->numweights; i++) + { + if (!strcmp(name, config->weights[i].name)) + { + ScaleFuzzySeperator_r(config->weights[i].firstseperator, scale); + break; + } //end if + } //end for +} //end of the function ScaleWeight +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ScaleFuzzySeperatorBalanceRange_r(fuzzyseperator_t *fs, float scale) +{ + if (fs->child) + { + ScaleFuzzySeperatorBalanceRange_r(fs->child, scale); + } //end if + else if (fs->type == WT_BALANCE) + { + float mid = (fs->minweight + fs->maxweight) * 0.5; + //get the weight between bounds + fs->maxweight = mid + (fs->maxweight - mid) * scale; + fs->minweight = mid + (fs->minweight - mid) * scale; + if (fs->maxweight < fs->minweight) + { + fs->maxweight = fs->minweight; + } //end if + } //end else if + if (fs->next) ScaleFuzzySeperatorBalanceRange_r(fs->next, scale); +} //end of the function ScaleFuzzySeperatorBalanceRange_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ScaleFuzzyBalanceRange(weightconfig_t *config, float scale) +{ + int i; + + if (scale < 0) scale = 0; + else if (scale > 100) scale = 100; + for (i = 0; i < config->numweights; i++) + { + ScaleFuzzySeperatorBalanceRange_r(config->weights[i].firstseperator, scale); + } //end for +} //end of the function ScaleFuzzyBalanceRange +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int InterbreedFuzzySeperator_r(fuzzyseperator_t *fs1, fuzzyseperator_t *fs2, + fuzzyseperator_t *fsout) +{ + if (fs1->child) + { + if (!fs2->child || !fsout->child) + { + botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal child\n"); + return qfalse; + } //end if + if (!InterbreedFuzzySeperator_r(fs2->child, fs2->child, fsout->child)) + { + return qfalse; + } //end if + } //end if + else if (fs1->type == WT_BALANCE) + { + if (fs2->type != WT_BALANCE || fsout->type != WT_BALANCE) + { + botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal balance\n"); + return qfalse; + } //end if + fsout->weight = (fs1->weight + fs2->weight) / 2; + if (fsout->weight > fsout->maxweight) fsout->maxweight = fsout->weight; + if (fsout->weight > fsout->minweight) fsout->minweight = fsout->weight; + } //end else if + if (fs1->next) + { + if (!fs2->next || !fsout->next) + { + botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal next\n"); + return qfalse; + } //end if + if (!InterbreedFuzzySeperator_r(fs1->next, fs2->next, fsout->next)) + { + return qfalse; + } //end if + } //end if + return qtrue; +} //end of the function InterbreedFuzzySeperator_r +//=========================================================================== +// config1 and config2 are interbreeded and stored in configout +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2, + weightconfig_t *configout) +{ + int i; + + if (config1->numweights != config2->numweights || + config1->numweights != configout->numweights) + { + botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal numweights\n"); + return; + } //end if + for (i = 0; i < config1->numweights; i++) + { + InterbreedFuzzySeperator_r(config1->weights[i].firstseperator, + config2->weights[i].firstseperator, + configout->weights[i].firstseperator); + } //end for +} //end of the function InterbreedWeightConfigs +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotShutdownWeights(void) +{ + int i; + + for( i = 0; i < MAX_WEIGHT_FILES; i++ ) + { + if (weightFileList[i]) + { + FreeWeightConfig2(weightFileList[i]); + weightFileList[i] = NULL; + } //end if + } //end for +} //end of the function BotShutdownWeights diff --git a/reaction/engine/code/botlib/be_ai_weight.h b/reaction/engine/code/botlib/be_ai_weight.h new file mode 100644 index 00000000..fb1c8853 --- /dev/null +++ b/reaction/engine/code/botlib/be_ai_weight.h @@ -0,0 +1,83 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_weight.h + * + * desc: fuzzy weights + * + * $Archive: /source/code/botlib/be_ai_weight.h $ + * + *****************************************************************************/ + +#define WT_BALANCE 1 +#define MAX_WEIGHTS 128 + +//fuzzy seperator +typedef struct fuzzyseperator_s +{ + int index; + int value; + int type; + float weight; + float minweight; + float maxweight; + struct fuzzyseperator_s *child; + struct fuzzyseperator_s *next; +} fuzzyseperator_t; + +//fuzzy weight +typedef struct weight_s +{ + char *name; + struct fuzzyseperator_s *firstseperator; +} weight_t; + +//weight configuration +typedef struct weightconfig_s +{ + int numweights; + weight_t weights[MAX_WEIGHTS]; + char filename[MAX_QPATH]; +} weightconfig_t; + +//reads a weight configuration +weightconfig_t *ReadWeightConfig(char *filename); +//free a weight configuration +void FreeWeightConfig(weightconfig_t *config); +//writes a weight configuration, returns true if successfull +qboolean WriteWeightConfig(char *filename, weightconfig_t *config); +//find the fuzzy weight with the given name +int FindFuzzyWeight(weightconfig_t *wc, char *name); +//returns the fuzzy weight for the given inventory and weight +float FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum); +float FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum); +//scales the weight with the given name +void ScaleWeight(weightconfig_t *config, char *name, float scale); +//scale the balance range +void ScaleBalanceRange(weightconfig_t *config, float scale); +//evolves the weight configuration +void EvolveWeightConfig(weightconfig_t *config); +//interbreed the weight configurations and stores the interbreeded one in configout +void InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2, weightconfig_t *configout); +//frees cached weight configurations +void BotShutdownWeights(void); diff --git a/reaction/engine/code/botlib/be_ea.c b/reaction/engine/code/botlib/be_ea.c new file mode 100644 index 00000000..3e284197 --- /dev/null +++ b/reaction/engine/code/botlib/be_ea.c @@ -0,0 +1,508 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ea.c + * + * desc: elementary actions + * + * $Archive: /MissionPack/code/botlib/be_ea.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "botlib.h" +#include "be_interface.h" + +#define MAX_USERMOVE 400 +#define MAX_COMMANDARGUMENTS 10 +#define ACTION_JUMPEDLASTFRAME 128 + +bot_input_t *botinputs; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Say(int client, char *str) +{ + botimport.BotClientCommand(client, va("say %s", str) ); +} //end of the function EA_Say +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_SayTeam(int client, char *str) +{ + botimport.BotClientCommand(client, va("say_team %s", str)); +} //end of the function EA_SayTeam +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Tell(int client, int clientto, char *str) +{ + botimport.BotClientCommand(client, va("tell %d, %s", clientto, str)); +} //end of the function EA_SayTeam +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_UseItem(int client, char *it) +{ + botimport.BotClientCommand(client, va("use %s", it)); +} //end of the function EA_UseItem +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_DropItem(int client, char *it) +{ + botimport.BotClientCommand(client, va("drop %s", it)); +} //end of the function EA_DropItem +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_UseInv(int client, char *inv) +{ + botimport.BotClientCommand(client, va("invuse %s", inv)); +} //end of the function EA_UseInv +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_DropInv(int client, char *inv) +{ + botimport.BotClientCommand(client, va("invdrop %s", inv)); +} //end of the function EA_DropInv +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Gesture(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_GESTURE; +} //end of the function EA_Gesture +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Command(int client, char *command) +{ + botimport.BotClientCommand(client, command); +} //end of the function EA_Command +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_SelectWeapon(int client, int weapon) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->weapon = weapon; +} //end of the function EA_SelectWeapon +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Attack(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_ATTACK; +} //end of the function EA_Attack +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Talk(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_TALK; +} //end of the function EA_Talk +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Use(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_USE; +} //end of the function EA_Use +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Respawn(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_RESPAWN; +} //end of the function EA_Respawn +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Jump(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + if (bi->actionflags & ACTION_JUMPEDLASTFRAME) + { + bi->actionflags &= ~ACTION_JUMP; + } //end if + else + { + bi->actionflags |= ACTION_JUMP; + } //end if +} //end of the function EA_Jump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_DelayedJump(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + if (bi->actionflags & ACTION_JUMPEDLASTFRAME) + { + bi->actionflags &= ~ACTION_DELAYEDJUMP; + } //end if + else + { + bi->actionflags |= ACTION_DELAYEDJUMP; + } //end if +} //end of the function EA_DelayedJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Crouch(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_CROUCH; +} //end of the function EA_Crouch +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Walk(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_WALK; +} //end of the function EA_Walk +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Action(int client, int action) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= action; +} //end of function EA_Action +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_MoveUp(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_MOVEUP; +} //end of the function EA_MoveUp +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_MoveDown(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_MOVEDOWN; +} //end of the function EA_MoveDown +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_MoveForward(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_MOVEFORWARD; +} //end of the function EA_MoveForward +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_MoveBack(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_MOVEBACK; +} //end of the function EA_MoveBack +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_MoveLeft(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_MOVELEFT; +} //end of the function EA_MoveLeft +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_MoveRight(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_MOVERIGHT; +} //end of the function EA_MoveRight +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Move(int client, vec3_t dir, float speed) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + VectorCopy(dir, bi->dir); + //cap speed + if (speed > MAX_USERMOVE) speed = MAX_USERMOVE; + else if (speed < -MAX_USERMOVE) speed = -MAX_USERMOVE; + bi->speed = speed; +} //end of the function EA_Move +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_View(int client, vec3_t viewangles) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + VectorCopy(viewangles, bi->viewangles); +} //end of the function EA_View +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_EndRegular(int client, float thinktime) +{ +/* + bot_input_t *bi; + int jumped = qfalse; + + bi = &botinputs[client]; + + bi->actionflags &= ~ACTION_JUMPEDLASTFRAME; + + bi->thinktime = thinktime; + botimport.BotInput(client, bi); + + bi->thinktime = 0; + VectorClear(bi->dir); + bi->speed = 0; + jumped = bi->actionflags & ACTION_JUMP; + bi->actionflags = 0; + if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME; +*/ +} //end of the function EA_EndRegular +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_GetInput(int client, float thinktime, bot_input_t *input) +{ + bot_input_t *bi; +// int jumped = qfalse; + + bi = &botinputs[client]; + +// bi->actionflags &= ~ACTION_JUMPEDLASTFRAME; + + bi->thinktime = thinktime; + Com_Memcpy(input, bi, sizeof(bot_input_t)); + + /* + bi->thinktime = 0; + VectorClear(bi->dir); + bi->speed = 0; + jumped = bi->actionflags & ACTION_JUMP; + bi->actionflags = 0; + if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME; + */ +} //end of the function EA_GetInput +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_ResetInput(int client) +{ + bot_input_t *bi; + int jumped = qfalse; + + bi = &botinputs[client]; + bi->actionflags &= ~ACTION_JUMPEDLASTFRAME; + + bi->thinktime = 0; + VectorClear(bi->dir); + bi->speed = 0; + jumped = bi->actionflags & ACTION_JUMP; + bi->actionflags = 0; + if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME; +} //end of the function EA_ResetInput +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int EA_Setup(void) +{ + //initialize the bot inputs + botinputs = (bot_input_t *) GetClearedHunkMemory( + botlibglobals.maxclients * sizeof(bot_input_t)); + return BLERR_NOERROR; +} //end of the function EA_Setup +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Shutdown(void) +{ + FreeMemory(botinputs); + botinputs = NULL; +} //end of the function EA_Shutdown diff --git a/reaction/engine/code/botlib/be_ea.h b/reaction/engine/code/botlib/be_ea.h new file mode 100644 index 00000000..4fb37046 --- /dev/null +++ b/reaction/engine/code/botlib/be_ea.h @@ -0,0 +1,66 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: be_ea.h + * + * desc: elementary actions + * + * $Archive: /source/code/botlib/be_ea.h $ + * + *****************************************************************************/ + +//ClientCommand elementary actions +void EA_Say(int client, char *str); +void EA_SayTeam(int client, char *str); +void EA_Command(int client, char *command ); + +void EA_Action(int client, int action); +void EA_Crouch(int client); +void EA_Walk(int client); +void EA_MoveUp(int client); +void EA_MoveDown(int client); +void EA_MoveForward(int client); +void EA_MoveBack(int client); +void EA_MoveLeft(int client); +void EA_MoveRight(int client); +void EA_Attack(int client); +void EA_Respawn(int client); +void EA_Talk(int client); +void EA_Gesture(int client); +void EA_Use(int client); + +//regular elementary actions +void EA_SelectWeapon(int client, int weapon); +void EA_Jump(int client); +void EA_DelayedJump(int client); +void EA_Move(int client, vec3_t dir, float speed); +void EA_View(int client, vec3_t viewangles); + +//send regular input to the server +void EA_EndRegular(int client, float thinktime); +void EA_GetInput(int client, float thinktime, bot_input_t *input); +void EA_ResetInput(int client); +//setup and shutdown routines +int EA_Setup(void); +void EA_Shutdown(void); diff --git a/reaction/engine/code/botlib/be_interface.c b/reaction/engine/code/botlib/be_interface.c new file mode 100644 index 00000000..5418635b --- /dev/null +++ b/reaction/engine/code/botlib/be_interface.c @@ -0,0 +1,895 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_interface.c + * + * desc: bot library interface + * + * $Archive: /MissionPack/code/botlib/be_interface.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_log.h" +#include "l_libvar.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "botlib.h" +#include "be_aas.h" +#include "be_aas_funcs.h" +#include "be_aas_def.h" +#include "be_interface.h" + +#include "be_ea.h" +#include "be_ai_weight.h" +#include "be_ai_goal.h" +#include "be_ai_move.h" +#include "be_ai_weap.h" +#include "be_ai_chat.h" +#include "be_ai_char.h" +#include "be_ai_gen.h" + +//library globals in a structure +botlib_globals_t botlibglobals; + +botlib_export_t be_botlib_export; +botlib_import_t botimport; +// +int bot_developer; +//qtrue if the library is setup +int botlibsetup = qfalse; + +//=========================================================================== +// +// several functions used by the exported functions +// +//=========================================================================== + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Sys_MilliSeconds(void) +{ + return clock() * 1000 / CLOCKS_PER_SEC; +} //end of the function Sys_MilliSeconds +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean ValidClientNumber(int num, char *str) +{ + if (num < 0 || num > botlibglobals.maxclients) + { + //weird: the disabled stuff results in a crash + botimport.Print(PRT_ERROR, "%s: invalid client number %d, [0, %d]\n", + str, num, botlibglobals.maxclients); + return qfalse; + } //end if + return qtrue; +} //end of the function BotValidateClientNumber +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean ValidEntityNumber(int num, char *str) +{ + if (num < 0 || num > botlibglobals.maxentities) + { + botimport.Print(PRT_ERROR, "%s: invalid entity number %d, [0, %d]\n", + str, num, botlibglobals.maxentities); + return qfalse; + } //end if + return qtrue; +} //end of the function BotValidateClientNumber +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean BotLibSetup(char *str) +{ + if (!botlibglobals.botlibsetup) + { + botimport.Print(PRT_ERROR, "%s: bot library used before being setup\n", str); + return qfalse; + } //end if + return qtrue; +} //end of the function BotLibSetup + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Export_BotLibSetup(void) +{ + int errnum; + char logfilename[MAX_OSPATH]; + char *homedir, *gamedir; + + bot_developer = LibVarGetValue("bot_developer"); + memset( &botlibglobals, 0, sizeof(botlibglobals) ); + //initialize byte swapping (litte endian etc.) +// Swap_Init(); + homedir = LibVarGetString("homedir"); + gamedir = LibVarGetString("gamedir"); + if (homedir[0]) { + if (gamedir[0]) { + Com_sprintf(logfilename, sizeof(logfilename), "%s%c%s%cbotlib.log", homedir, PATH_SEP, gamedir, PATH_SEP); + } + else { + Com_sprintf(logfilename, sizeof(logfilename), "%s%c" BASEGAME "%cbotlib.log", homedir, PATH_SEP, PATH_SEP); + } + } else { + Com_sprintf(logfilename, sizeof(logfilename), "botlib.log"); + } + Log_Open(logfilename); + // + botimport.Print(PRT_MESSAGE, "------- BotLib Initialization -------\n"); + // + botlibglobals.maxclients = (int) LibVarValue("maxclients", "128"); + botlibglobals.maxentities = (int) LibVarValue("maxentities", "1024"); + + errnum = AAS_Setup(); //be_aas_main.c + if (errnum != BLERR_NOERROR) return errnum; + errnum = EA_Setup(); //be_ea.c + if (errnum != BLERR_NOERROR) return errnum; + errnum = BotSetupWeaponAI(); //be_ai_weap.c + if (errnum != BLERR_NOERROR)return errnum; + errnum = BotSetupGoalAI(); //be_ai_goal.c + if (errnum != BLERR_NOERROR) return errnum; + errnum = BotSetupChatAI(); //be_ai_chat.c + if (errnum != BLERR_NOERROR) return errnum; + errnum = BotSetupMoveAI(); //be_ai_move.c + if (errnum != BLERR_NOERROR) return errnum; + + botlibsetup = qtrue; + botlibglobals.botlibsetup = qtrue; + + return BLERR_NOERROR; +} //end of the function Export_BotLibSetup +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Export_BotLibShutdown(void) +{ + if (!BotLibSetup("BotLibShutdown")) return BLERR_LIBRARYNOTSETUP; +#ifndef DEMO + //DumpFileCRCs(); +#endif //DEMO + // + BotShutdownChatAI(); //be_ai_chat.c + BotShutdownMoveAI(); //be_ai_move.c + BotShutdownGoalAI(); //be_ai_goal.c + BotShutdownWeaponAI(); //be_ai_weap.c + BotShutdownWeights(); //be_ai_weight.c + BotShutdownCharacters(); //be_ai_char.c + //shud down aas + AAS_Shutdown(); + //shut down bot elemantary actions + EA_Shutdown(); + //free all libvars + LibVarDeAllocAll(); + //remove all global defines from the pre compiler + PC_RemoveAllGlobalDefines(); + + //dump all allocated memory +// DumpMemory(); +#ifdef DEBUG + PrintMemoryLabels(); +#endif + //shut down library log file + Log_Shutdown(); + // + botlibsetup = qfalse; + botlibglobals.botlibsetup = qfalse; + // print any files still open + PC_CheckOpenSourceHandles(); + // + return BLERR_NOERROR; +} //end of the function Export_BotLibShutdown +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Export_BotLibVarSet(char *var_name, char *value) +{ + LibVarSet(var_name, value); + return BLERR_NOERROR; +} //end of the function Export_BotLibVarSet +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Export_BotLibVarGet(char *var_name, char *value, int size) +{ + char *varvalue; + + varvalue = LibVarGetString(var_name); + strncpy(value, varvalue, size-1); + value[size-1] = '\0'; + return BLERR_NOERROR; +} //end of the function Export_BotLibVarGet +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Export_BotLibStartFrame(float time) +{ + if (!BotLibSetup("BotStartFrame")) return BLERR_LIBRARYNOTSETUP; + return AAS_StartFrame(time); +} //end of the function Export_BotLibStartFrame +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Export_BotLibLoadMap(const char *mapname) +{ +#ifdef DEBUG + int starttime = Sys_MilliSeconds(); +#endif + int errnum; + + if (!BotLibSetup("BotLoadMap")) return BLERR_LIBRARYNOTSETUP; + // + botimport.Print(PRT_MESSAGE, "------------ Map Loading ------------\n"); + //startup AAS for the current map, model and sound index + errnum = AAS_LoadMap(mapname); + if (errnum != BLERR_NOERROR) return errnum; + //initialize the items in the level + BotInitLevelItems(); //be_ai_goal.h + BotSetBrushModelTypes(); //be_ai_move.h + // + botimport.Print(PRT_MESSAGE, "-------------------------------------\n"); +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "map loaded in %d msec\n", Sys_MilliSeconds() - starttime); +#endif + // + return BLERR_NOERROR; +} //end of the function Export_BotLibLoadMap +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Export_BotLibUpdateEntity(int ent, bot_entitystate_t *state) +{ + if (!BotLibSetup("BotUpdateEntity")) return BLERR_LIBRARYNOTSETUP; + if (!ValidEntityNumber(ent, "BotUpdateEntity")) return BLERR_INVALIDENTITYNUMBER; + + return AAS_UpdateEntity(ent, state); +} //end of the function Export_BotLibUpdateEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_TestMovementPrediction(int entnum, vec3_t origin, vec3_t dir); +void ElevatorBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter); +int BotGetReachabilityToGoal(vec3_t origin, int areanum, + int lastgoalareanum, int lastareanum, + int *avoidreach, float *avoidreachtimes, int *avoidreachtries, + bot_goal_t *goal, int travelflags, int movetravelflags, + struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags); + +int AAS_PointLight(vec3_t origin, int *red, int *green, int *blue); + +int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas); + +int AAS_Reachability_WeaponJump(int area1num, int area2num); + +int BotFuzzyPointReachabilityArea(vec3_t origin); + +float BotGapDistance(vec3_t origin, vec3_t hordir, int entnum); + +void AAS_FloodAreas(vec3_t origin); + +int BotExportTest(int parm0, char *parm1, vec3_t parm2, vec3_t parm3) +{ + +// return AAS_PointLight(parm2, NULL, NULL, NULL); + +#ifdef DEBUG + static int area = -1; + static int line[2]; + int newarea, i, highlightarea, flood; +// int reachnum; + vec3_t eye, forward, right, end, origin; +// vec3_t bottomcenter; +// aas_trace_t trace; +// aas_face_t *face; +// aas_entity_t *ent; +// bsp_trace_t bsptrace; +// aas_reachability_t reach; +// bot_goal_t goal; + + // clock_t start_time, end_time; + vec3_t mins = {-16, -16, -24}; + vec3_t maxs = {16, 16, 32}; + +// int areas[10], numareas; + + + //return 0; + + if (!aasworld.loaded) return 0; + + /* + if (parm0 & 1) + { + AAS_ClearShownPolygons(); + AAS_FloodAreas(parm2); + } //end if + return 0; + */ + for (i = 0; i < 2; i++) if (!line[i]) line[i] = botimport.DebugLineCreate(); + +// AAS_ClearShownDebugLines(); + + //if (AAS_AgainstLadder(parm2)) botimport.Print(PRT_MESSAGE, "against ladder\n"); + //BotOnGround(parm2, PRESENCE_NORMAL, 1, &newarea, &newarea); + //botimport.Print(PRT_MESSAGE, "%f %f %f\n", parm2[0], parm2[1], parm2[2]); + //* + highlightarea = LibVarGetValue("bot_highlightarea"); + if (highlightarea > 0) + { + newarea = highlightarea; + } //end if + else + { + VectorCopy(parm2, origin); + origin[2] += 0.5; + //newarea = AAS_PointAreaNum(origin); + newarea = BotFuzzyPointReachabilityArea(origin); + } //end else + + botimport.Print(PRT_MESSAGE, "\rtravel time to goal (%d) = %d ", botlibglobals.goalareanum, + AAS_AreaTravelTimeToGoalArea(newarea, origin, botlibglobals.goalareanum, TFL_DEFAULT)); + //newarea = BotReachabilityArea(origin, qtrue); + if (newarea != area) + { + botimport.Print(PRT_MESSAGE, "origin = %f, %f, %f\n", origin[0], origin[1], origin[2]); + area = newarea; + botimport.Print(PRT_MESSAGE, "new area %d, cluster %d, presence type %d\n", + area, AAS_AreaCluster(area), AAS_PointPresenceType(origin)); + botimport.Print(PRT_MESSAGE, "area contents: "); + if (aasworld.areasettings[area].contents & AREACONTENTS_WATER) + { + botimport.Print(PRT_MESSAGE, "water &"); + } //end if + if (aasworld.areasettings[area].contents & AREACONTENTS_LAVA) + { + botimport.Print(PRT_MESSAGE, "lava &"); + } //end if + if (aasworld.areasettings[area].contents & AREACONTENTS_SLIME) + { + botimport.Print(PRT_MESSAGE, "slime &"); + } //end if + if (aasworld.areasettings[area].contents & AREACONTENTS_JUMPPAD) + { + botimport.Print(PRT_MESSAGE, "jump pad &"); + } //end if + if (aasworld.areasettings[area].contents & AREACONTENTS_CLUSTERPORTAL) + { + botimport.Print(PRT_MESSAGE, "cluster portal &"); + } //end if + if (aasworld.areasettings[area].contents & AREACONTENTS_VIEWPORTAL) + { + botimport.Print(PRT_MESSAGE, "view portal &"); + } //end if + if (aasworld.areasettings[area].contents & AREACONTENTS_DONOTENTER) + { + botimport.Print(PRT_MESSAGE, "do not enter &"); + } //end if + if (aasworld.areasettings[area].contents & AREACONTENTS_MOVER) + { + botimport.Print(PRT_MESSAGE, "mover &"); + } //end if + if (!aasworld.areasettings[area].contents) + { + botimport.Print(PRT_MESSAGE, "empty"); + } //end if + botimport.Print(PRT_MESSAGE, "\n"); + botimport.Print(PRT_MESSAGE, "travel time to goal (%d) = %d\n", botlibglobals.goalareanum, + AAS_AreaTravelTimeToGoalArea(newarea, origin, botlibglobals.goalareanum, TFL_DEFAULT|TFL_ROCKETJUMP)); + /* + VectorCopy(origin, end); + end[2] += 5; + numareas = AAS_TraceAreas(origin, end, areas, NULL, 10); + AAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1); + botimport.Print(PRT_MESSAGE, "num areas = %d, area = %d\n", numareas, areas[0]); + */ + /* + botlibglobals.goalareanum = newarea; + VectorCopy(parm2, botlibglobals.goalorigin); + botimport.Print(PRT_MESSAGE, "new goal %2.1f %2.1f %2.1f area %d\n", + origin[0], origin[1], origin[2], newarea); + */ + } //end if + //* + flood = LibVarGetValue("bot_flood"); + if (parm0 & 1) + { + if (flood) + { + AAS_ClearShownPolygons(); + AAS_ClearShownDebugLines(); + AAS_FloodAreas(parm2); + } + else + { + botlibglobals.goalareanum = newarea; + VectorCopy(parm2, botlibglobals.goalorigin); + botimport.Print(PRT_MESSAGE, "new goal %2.1f %2.1f %2.1f area %d\n", + origin[0], origin[1], origin[2], newarea); + } + } //end if*/ + if (flood) + return 0; +// if (parm0 & BUTTON_USE) +// { +// botlibglobals.runai = !botlibglobals.runai; +// if (botlibglobals.runai) botimport.Print(PRT_MESSAGE, "started AI\n"); +// else botimport.Print(PRT_MESSAGE, "stopped AI\n"); + //* / + /* + goal.areanum = botlibglobals.goalareanum; + reachnum = BotGetReachabilityToGoal(parm2, newarea, 1, + ms.avoidreach, ms.avoidreachtimes, + &goal, TFL_DEFAULT); + if (!reachnum) + { + botimport.Print(PRT_MESSAGE, "goal not reachable\n"); + } //end if + else + { + AAS_ReachabilityFromNum(reachnum, &reach); + AAS_ClearShownDebugLines(); + AAS_ShowArea(area, qtrue); + AAS_ShowArea(reach.areanum, qtrue); + AAS_DrawCross(reach.start, 6, LINECOLOR_BLUE); + AAS_DrawCross(reach.end, 6, LINECOLOR_RED); + // + if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) + { + ElevatorBottomCenter(&reach, bottomcenter); + AAS_DrawCross(bottomcenter, 10, LINECOLOR_GREEN); + } //end if + } //end else*/ +// botimport.Print(PRT_MESSAGE, "travel time to goal = %d\n", +// AAS_AreaTravelTimeToGoalArea(area, origin, botlibglobals.goalareanum, TFL_DEFAULT)); +// botimport.Print(PRT_MESSAGE, "test rj from 703 to 716\n"); +// AAS_Reachability_WeaponJump(703, 716); +// } //end if*/ + +/* face = AAS_AreaGroundFace(newarea, parm2); + if (face) + { + AAS_ShowFace(face - aasworld.faces); + } //end if*/ + /* + AAS_ClearShownDebugLines(); + AAS_ShowArea(newarea, parm0 & BUTTON_USE); + AAS_ShowReachableAreas(area); + */ + AAS_ClearShownPolygons(); + AAS_ClearShownDebugLines(); + AAS_ShowAreaPolygons(newarea, 1, parm0 & 4); + if (parm0 & 2) AAS_ShowReachableAreas(area); + else + { + static int lastgoalareanum, lastareanum; + static int avoidreach[MAX_AVOIDREACH]; + static float avoidreachtimes[MAX_AVOIDREACH]; + static int avoidreachtries[MAX_AVOIDREACH]; + int reachnum, resultFlags; + bot_goal_t goal; + aas_reachability_t reach; + + /* + goal.areanum = botlibglobals.goalareanum; + VectorCopy(botlibglobals.goalorigin, goal.origin); + reachnum = BotGetReachabilityToGoal(origin, newarea, + lastgoalareanum, lastareanum, + avoidreach, avoidreachtimes, avoidreachtries, + &goal, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, + NULL, 0, &resultFlags); + AAS_ReachabilityFromNum(reachnum, &reach); + AAS_ShowReachability(&reach); + */ + int curarea; + vec3_t curorigin; + + goal.areanum = botlibglobals.goalareanum; + VectorCopy(botlibglobals.goalorigin, goal.origin); + VectorCopy(origin, curorigin); + curarea = newarea; + for ( i = 0; i < 100; i++ ) { + if ( curarea == goal.areanum ) { + break; + } + reachnum = BotGetReachabilityToGoal(curorigin, curarea, + lastgoalareanum, lastareanum, + avoidreach, avoidreachtimes, avoidreachtries, + &goal, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, + NULL, 0, &resultFlags); + AAS_ReachabilityFromNum(reachnum, &reach); + AAS_ShowReachability(&reach); + VectorCopy(reach.end, origin); + lastareanum = curarea; + curarea = reach.areanum; + } + } //end else + VectorClear(forward); + //BotGapDistance(origin, forward, 0); + /* + if (parm0 & BUTTON_USE) + { + botimport.Print(PRT_MESSAGE, "test rj from 703 to 716\n"); + AAS_Reachability_WeaponJump(703, 716); + } //end if*/ + + AngleVectors(parm3, forward, right, NULL); + //get the eye 16 units to the right of the origin + VectorMA(parm2, 8, right, eye); + //get the eye 24 units up + eye[2] += 24; + //get the end point for the line to be traced + VectorMA(eye, 800, forward, end); + +// AAS_TestMovementPrediction(1, parm2, forward); +/* + //trace the line to find the hit point + trace = AAS_TraceClientBBox(eye, end, PRESENCE_NORMAL, 1); + if (!line[0]) line[0] = botimport.DebugLineCreate(); + botimport.DebugLineShow(line[0], eye, trace.endpos, LINECOLOR_BLUE); + // + AAS_ClearShownDebugLines(); + if (trace.ent) + { + ent = &aasworld.entities[trace.ent]; + AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs); + } //end if +*/ + +/* + start_time = clock(); + for (i = 0; i < 2000; i++) + { + AAS_Trace2(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); +// AAS_TraceClientBBox(eye, end, PRESENCE_NORMAL, 1); + } //end for + end_time = clock(); + botimport.Print(PRT_MESSAGE, "me %lu clocks, %lu CLOCKS_PER_SEC\n", end_time - start_time, CLOCKS_PER_SEC); + start_time = clock(); + for (i = 0; i < 2000; i++) + { + AAS_Trace(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); + } //end for + end_time = clock(); + botimport.Print(PRT_MESSAGE, "id %lu clocks, %lu CLOCKS_PER_SEC\n", end_time - start_time, CLOCKS_PER_SEC); +*/ + + // TTimo: nested comments are BAD for gcc -Werror, use #if 0 instead.. +#if 0 + AAS_ClearShownDebugLines(); + //bsptrace = AAS_Trace(eye, NULL, NULL, end, 1, MASK_PLAYERSOLID); + bsptrace = AAS_Trace(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); + if (!line[0]) line[0] = botimport.DebugLineCreate(); + botimport.DebugLineShow(line[0], eye, bsptrace.endpos, LINECOLOR_YELLOW); + if (bsptrace.fraction < 1.0) + { + face = AAS_TraceEndFace(&trace); + if (face) + { + AAS_ShowFace(face - aasworld.faces); + } //end if + + AAS_DrawPlaneCross(bsptrace.endpos, + bsptrace.plane.normal, + bsptrace.plane.dist + bsptrace.exp_dist, + bsptrace.plane.type, LINECOLOR_GREEN); + if (trace.ent) + { + ent = &aasworld.entities[trace.ent]; + AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs); + } //end if + } //end if + //bsptrace = AAS_Trace2(eye, NULL, NULL, end, 1, MASK_PLAYERSOLID); + bsptrace = AAS_Trace2(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); + botimport.DebugLineShow(line[1], eye, bsptrace.endpos, LINECOLOR_BLUE); + if (bsptrace.fraction < 1.0) + { + AAS_DrawPlaneCross(bsptrace.endpos, + bsptrace.plane.normal, + bsptrace.plane.dist,// + bsptrace.exp_dist, + bsptrace.plane.type, LINECOLOR_RED); + if (bsptrace.ent) + { + ent = &aasworld.entities[bsptrace.ent]; + AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs); + } //end if + } //end if +#endif +#endif + return 0; +} //end of the function BotExportTest + + +/* +============ +Init_AAS_Export +============ +*/ +static void Init_AAS_Export( aas_export_t *aas ) { + //-------------------------------------------- + // be_aas_entity.c + //-------------------------------------------- + aas->AAS_EntityInfo = AAS_EntityInfo; + //-------------------------------------------- + // be_aas_main.c + //-------------------------------------------- + aas->AAS_Initialized = AAS_Initialized; + aas->AAS_PresenceTypeBoundingBox = AAS_PresenceTypeBoundingBox; + aas->AAS_Time = AAS_Time; + //-------------------------------------------- + // be_aas_sample.c + //-------------------------------------------- + aas->AAS_PointAreaNum = AAS_PointAreaNum; + aas->AAS_PointReachabilityAreaIndex = AAS_PointReachabilityAreaIndex; + aas->AAS_TraceAreas = AAS_TraceAreas; + aas->AAS_BBoxAreas = AAS_BBoxAreas; + aas->AAS_AreaInfo = AAS_AreaInfo; + //-------------------------------------------- + // be_aas_bspq3.c + //-------------------------------------------- + aas->AAS_PointContents = AAS_PointContents; + aas->AAS_NextBSPEntity = AAS_NextBSPEntity; + aas->AAS_ValueForBSPEpairKey = AAS_ValueForBSPEpairKey; + aas->AAS_VectorForBSPEpairKey = AAS_VectorForBSPEpairKey; + aas->AAS_FloatForBSPEpairKey = AAS_FloatForBSPEpairKey; + aas->AAS_IntForBSPEpairKey = AAS_IntForBSPEpairKey; + //-------------------------------------------- + // be_aas_reach.c + //-------------------------------------------- + aas->AAS_AreaReachability = AAS_AreaReachability; + //-------------------------------------------- + // be_aas_route.c + //-------------------------------------------- + aas->AAS_AreaTravelTimeToGoalArea = AAS_AreaTravelTimeToGoalArea; + aas->AAS_EnableRoutingArea = AAS_EnableRoutingArea; + aas->AAS_PredictRoute = AAS_PredictRoute; + //-------------------------------------------- + // be_aas_altroute.c + //-------------------------------------------- + aas->AAS_AlternativeRouteGoals = AAS_AlternativeRouteGoals; + //-------------------------------------------- + // be_aas_move.c + //-------------------------------------------- + aas->AAS_Swimming = AAS_Swimming; + aas->AAS_PredictClientMovement = AAS_PredictClientMovement; +} + + +/* +============ +Init_EA_Export +============ +*/ +static void Init_EA_Export( ea_export_t *ea ) { + //ClientCommand elementary actions + ea->EA_Command = EA_Command; + ea->EA_Say = EA_Say; + ea->EA_SayTeam = EA_SayTeam; + + ea->EA_Action = EA_Action; + ea->EA_Gesture = EA_Gesture; + ea->EA_Talk = EA_Talk; + ea->EA_Attack = EA_Attack; + ea->EA_Use = EA_Use; + ea->EA_Respawn = EA_Respawn; + ea->EA_Crouch = EA_Crouch; + ea->EA_MoveUp = EA_MoveUp; + ea->EA_MoveDown = EA_MoveDown; + ea->EA_MoveForward = EA_MoveForward; + ea->EA_MoveBack = EA_MoveBack; + ea->EA_MoveLeft = EA_MoveLeft; + ea->EA_MoveRight = EA_MoveRight; + + ea->EA_SelectWeapon = EA_SelectWeapon; + ea->EA_Jump = EA_Jump; + ea->EA_DelayedJump = EA_DelayedJump; + ea->EA_Move = EA_Move; + ea->EA_View = EA_View; + ea->EA_GetInput = EA_GetInput; + ea->EA_EndRegular = EA_EndRegular; + ea->EA_ResetInput = EA_ResetInput; +} + + +/* +============ +Init_AI_Export +============ +*/ +static void Init_AI_Export( ai_export_t *ai ) { + //----------------------------------- + // be_ai_char.h + //----------------------------------- + ai->BotLoadCharacter = BotLoadCharacter; + ai->BotFreeCharacter = BotFreeCharacter; + ai->Characteristic_Float = Characteristic_Float; + ai->Characteristic_BFloat = Characteristic_BFloat; + ai->Characteristic_Integer = Characteristic_Integer; + ai->Characteristic_BInteger = Characteristic_BInteger; + ai->Characteristic_String = Characteristic_String; + //----------------------------------- + // be_ai_chat.h + //----------------------------------- + ai->BotAllocChatState = BotAllocChatState; + ai->BotFreeChatState = BotFreeChatState; + ai->BotQueueConsoleMessage = BotQueueConsoleMessage; + ai->BotRemoveConsoleMessage = BotRemoveConsoleMessage; + ai->BotNextConsoleMessage = BotNextConsoleMessage; + ai->BotNumConsoleMessages = BotNumConsoleMessages; + ai->BotInitialChat = BotInitialChat; + ai->BotNumInitialChats = BotNumInitialChats; + ai->BotReplyChat = BotReplyChat; + ai->BotChatLength = BotChatLength; + ai->BotEnterChat = BotEnterChat; + ai->BotGetChatMessage = BotGetChatMessage; + ai->StringContains = StringContains; + ai->BotFindMatch = BotFindMatch; + ai->BotMatchVariable = BotMatchVariable; + ai->UnifyWhiteSpaces = UnifyWhiteSpaces; + ai->BotReplaceSynonyms = BotReplaceSynonyms; + ai->BotLoadChatFile = BotLoadChatFile; + ai->BotSetChatGender = BotSetChatGender; + ai->BotSetChatName = BotSetChatName; + //----------------------------------- + // be_ai_goal.h + //----------------------------------- + ai->BotResetGoalState = BotResetGoalState; + ai->BotResetAvoidGoals = BotResetAvoidGoals; + ai->BotRemoveFromAvoidGoals = BotRemoveFromAvoidGoals; + ai->BotPushGoal = BotPushGoal; + ai->BotPopGoal = BotPopGoal; + ai->BotEmptyGoalStack = BotEmptyGoalStack; + ai->BotDumpAvoidGoals = BotDumpAvoidGoals; + ai->BotDumpGoalStack = BotDumpGoalStack; + ai->BotGoalName = BotGoalName; + ai->BotGetTopGoal = BotGetTopGoal; + ai->BotGetSecondGoal = BotGetSecondGoal; + ai->BotChooseLTGItem = BotChooseLTGItem; + ai->BotChooseNBGItem = BotChooseNBGItem; + ai->BotTouchingGoal = BotTouchingGoal; + ai->BotItemGoalInVisButNotVisible = BotItemGoalInVisButNotVisible; + ai->BotGetLevelItemGoal = BotGetLevelItemGoal; + ai->BotGetNextCampSpotGoal = BotGetNextCampSpotGoal; + ai->BotGetMapLocationGoal = BotGetMapLocationGoal; + ai->BotAvoidGoalTime = BotAvoidGoalTime; + ai->BotSetAvoidGoalTime = BotSetAvoidGoalTime; + ai->BotInitLevelItems = BotInitLevelItems; + ai->BotUpdateEntityItems = BotUpdateEntityItems; + ai->BotLoadItemWeights = BotLoadItemWeights; + ai->BotFreeItemWeights = BotFreeItemWeights; + ai->BotInterbreedGoalFuzzyLogic = BotInterbreedGoalFuzzyLogic; + ai->BotSaveGoalFuzzyLogic = BotSaveGoalFuzzyLogic; + ai->BotMutateGoalFuzzyLogic = BotMutateGoalFuzzyLogic; + ai->BotAllocGoalState = BotAllocGoalState; + ai->BotFreeGoalState = BotFreeGoalState; + //----------------------------------- + // be_ai_move.h + //----------------------------------- + ai->BotResetMoveState = BotResetMoveState; + ai->BotMoveToGoal = BotMoveToGoal; + ai->BotMoveInDirection = BotMoveInDirection; + ai->BotResetAvoidReach = BotResetAvoidReach; + ai->BotResetLastAvoidReach = BotResetLastAvoidReach; + ai->BotReachabilityArea = BotReachabilityArea; + ai->BotMovementViewTarget = BotMovementViewTarget; + ai->BotPredictVisiblePosition = BotPredictVisiblePosition; + ai->BotAllocMoveState = BotAllocMoveState; + ai->BotFreeMoveState = BotFreeMoveState; + ai->BotInitMoveState = BotInitMoveState; + ai->BotAddAvoidSpot = BotAddAvoidSpot; + //----------------------------------- + // be_ai_weap.h + //----------------------------------- + ai->BotChooseBestFightWeapon = BotChooseBestFightWeapon; + ai->BotGetWeaponInfo = BotGetWeaponInfo; + ai->BotLoadWeaponWeights = BotLoadWeaponWeights; + ai->BotAllocWeaponState = BotAllocWeaponState; + ai->BotFreeWeaponState = BotFreeWeaponState; + ai->BotResetWeaponState = BotResetWeaponState; + //----------------------------------- + // be_ai_gen.h + //----------------------------------- + ai->GeneticParentsAndChildSelection = GeneticParentsAndChildSelection; +} + + +/* +============ +GetBotLibAPI +============ +*/ +botlib_export_t *GetBotLibAPI(int apiVersion, botlib_import_t *import) { + assert(import); + botimport = *import; + assert(botimport.Print); + + Com_Memset( &be_botlib_export, 0, sizeof( be_botlib_export ) ); + + if ( apiVersion != BOTLIB_API_VERSION ) { + botimport.Print( PRT_ERROR, "Mismatched BOTLIB_API_VERSION: expected %i, got %i\n", BOTLIB_API_VERSION, apiVersion ); + return NULL; + } + + Init_AAS_Export(&be_botlib_export.aas); + Init_EA_Export(&be_botlib_export.ea); + Init_AI_Export(&be_botlib_export.ai); + + be_botlib_export.BotLibSetup = Export_BotLibSetup; + be_botlib_export.BotLibShutdown = Export_BotLibShutdown; + be_botlib_export.BotLibVarSet = Export_BotLibVarSet; + be_botlib_export.BotLibVarGet = Export_BotLibVarGet; + + be_botlib_export.PC_AddGlobalDefine = PC_AddGlobalDefine; + be_botlib_export.PC_LoadSourceHandle = PC_LoadSourceHandle; + be_botlib_export.PC_FreeSourceHandle = PC_FreeSourceHandle; + be_botlib_export.PC_ReadTokenHandle = PC_ReadTokenHandle; + be_botlib_export.PC_SourceFileAndLine = PC_SourceFileAndLine; + + be_botlib_export.BotLibStartFrame = Export_BotLibStartFrame; + be_botlib_export.BotLibLoadMap = Export_BotLibLoadMap; + be_botlib_export.BotLibUpdateEntity = Export_BotLibUpdateEntity; + be_botlib_export.Test = BotExportTest; + + return &be_botlib_export; +} diff --git a/reaction/engine/code/botlib/be_interface.h b/reaction/engine/code/botlib/be_interface.h new file mode 100644 index 00000000..956530b3 --- /dev/null +++ b/reaction/engine/code/botlib/be_interface.h @@ -0,0 +1,57 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_interface.h + * + * desc: botlib interface + * + * $Archive: /source/code/botlib/be_interface.h $ + * + *****************************************************************************/ + +//#define DEBUG //debug code +#define RANDOMIZE //randomize bot behaviour + +//FIXME: get rid of this global structure +typedef struct botlib_globals_s +{ + int botlibsetup; //true when the bot library has been setup + int maxentities; //maximum number of entities + int maxclients; //maximum number of clients + float time; //the global time +#ifdef DEBUG + qboolean debug; //true if debug is on + int goalareanum; + vec3_t goalorigin; + int runai; +#endif +} botlib_globals_t; + + +extern botlib_globals_t botlibglobals; +extern botlib_import_t botimport; +extern int bot_developer; //true if developer is on + +// +int Sys_MilliSeconds(void); + diff --git a/reaction/engine/code/botlib/botlib.h b/reaction/engine/code/botlib/botlib.h new file mode 100644 index 00000000..a3f75161 --- /dev/null +++ b/reaction/engine/code/botlib/botlib.h @@ -0,0 +1,516 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +/***************************************************************************** + * name: botlib.h + * + * desc: bot AI library + * + * $Archive: /source/code/game/botai.h $ + * + *****************************************************************************/ + +#define BOTLIB_API_VERSION 2 + +struct aas_clientmove_s; +struct aas_entityinfo_s; +struct aas_areainfo_s; +struct aas_altroutegoal_s; +struct aas_predictroute_s; +struct bot_consolemessage_s; +struct bot_match_s; +struct bot_goal_s; +struct bot_moveresult_s; +struct bot_initmove_s; +struct weaponinfo_s; + +#define BOTFILESBASEFOLDER "botfiles" +//debug line colors +#define LINECOLOR_NONE -1 +#define LINECOLOR_RED 1//0xf2f2f0f0L +#define LINECOLOR_GREEN 2//0xd0d1d2d3L +#define LINECOLOR_BLUE 3//0xf3f3f1f1L +#define LINECOLOR_YELLOW 4//0xdcdddedfL +#define LINECOLOR_ORANGE 5//0xe0e1e2e3L + +//Print types +#define PRT_MESSAGE 1 +#define PRT_WARNING 2 +#define PRT_ERROR 3 +#define PRT_FATAL 4 +#define PRT_EXIT 5 + +//console message types +#define CMS_NORMAL 0 +#define CMS_CHAT 1 + +//botlib error codes +#define BLERR_NOERROR 0 //no error +#define BLERR_LIBRARYNOTSETUP 1 //library not setup +#define BLERR_INVALIDENTITYNUMBER 2 //invalid entity number +#define BLERR_NOAASFILE 3 //no AAS file available +#define BLERR_CANNOTOPENAASFILE 4 //cannot open AAS file +#define BLERR_WRONGAASFILEID 5 //incorrect AAS file id +#define BLERR_WRONGAASFILEVERSION 6 //incorrect AAS file version +#define BLERR_CANNOTREADAASLUMP 7 //cannot read AAS file lump +#define BLERR_CANNOTLOADICHAT 8 //cannot load initial chats +#define BLERR_CANNOTLOADITEMWEIGHTS 9 //cannot load item weights +#define BLERR_CANNOTLOADITEMCONFIG 10 //cannot load item config +#define BLERR_CANNOTLOADWEAPONWEIGHTS 11 //cannot load weapon weights +#define BLERR_CANNOTLOADWEAPONCONFIG 12 //cannot load weapon config + +//action flags +#define ACTION_ATTACK 0x0000001 +#define ACTION_USE 0x0000002 +#define ACTION_RESPAWN 0x0000008 +#define ACTION_JUMP 0x0000010 +#define ACTION_MOVEUP 0x0000020 +#define ACTION_CROUCH 0x0000080 +#define ACTION_MOVEDOWN 0x0000100 +#define ACTION_MOVEFORWARD 0x0000200 +#define ACTION_MOVEBACK 0x0000800 +#define ACTION_MOVELEFT 0x0001000 +#define ACTION_MOVERIGHT 0x0002000 +#define ACTION_DELAYEDJUMP 0x0008000 +#define ACTION_TALK 0x0010000 +#define ACTION_GESTURE 0x0020000 +#define ACTION_WALK 0x0080000 +#define ACTION_AFFIRMATIVE 0x0100000 +#define ACTION_NEGATIVE 0x0200000 +#define ACTION_GETFLAG 0x0800000 +#define ACTION_GUARDBASE 0x1000000 +#define ACTION_PATROL 0x2000000 +#define ACTION_FOLLOWME 0x8000000 + +//the bot input, will be converted to an usercmd_t +typedef struct bot_input_s +{ + float thinktime; //time since last output (in seconds) + vec3_t dir; //movement direction + float speed; //speed in the range [0, 400] + vec3_t viewangles; //the view angles + int actionflags; //one of the ACTION_? flags + int weapon; //weapon to use +} bot_input_t; + +#ifndef BSPTRACE + +#define BSPTRACE + +//bsp_trace_t hit surface +typedef struct bsp_surface_s +{ + char name[16]; + int flags; + int value; +} bsp_surface_t; + +//remove the bsp_trace_s structure definition l8r on +//a trace is returned when a box is swept through the world +typedef struct bsp_trace_s +{ + qboolean allsolid; // if true, plane is not valid + qboolean startsolid; // if true, the initial point was in a solid area + float fraction; // time completed, 1.0 = didn't hit anything + vec3_t endpos; // final position + cplane_t plane; // surface normal at impact + float exp_dist; // expanded plane distance + int sidenum; // number of the brush side hit + bsp_surface_t surface; // the hit point surface + int contents; // contents on other side of surface hit + int ent; // number of entity hit +} bsp_trace_t; + +#endif // BSPTRACE + +//entity state +typedef struct bot_entitystate_s +{ + int type; // entity type + int flags; // entity flags + vec3_t origin; // origin of the entity + vec3_t angles; // angles of the model + vec3_t old_origin; // for lerping + vec3_t mins; // bounding box minimums + vec3_t maxs; // bounding box maximums + int groundent; // ground entity + int solid; // solid type + int modelindex; // model used + int modelindex2; // weapons, CTF flags, etc + int frame; // model frame number + int event; // impulse events -- muzzle flashes, footsteps, etc + int eventParm; // even parameter + int powerups; // bit flags + int weapon; // determines weapon and flash model, etc + int legsAnim; // mask off ANIM_TOGGLEBIT + int torsoAnim; // mask off ANIM_TOGGLEBIT +} bot_entitystate_t; + +//bot AI library exported functions +typedef struct botlib_import_s +{ + //print messages from the bot library + void (QDECL *Print)(int type, char *fmt, ...); + //trace a bbox through the world + void (*Trace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask); + //trace a bbox against a specific entity + void (*EntityTrace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int entnum, int contentmask); + //retrieve the contents at the given point + int (*PointContents)(vec3_t point); + //check if the point is in potential visible sight + int (*inPVS)(vec3_t p1, vec3_t p2); + //retrieve the BSP entity data lump + char *(*BSPEntityData)(void); + // + void (*BSPModelMinsMaxsOrigin)(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin); + //send a bot client command + void (*BotClientCommand)(int client, char *command); + //memory allocation + void *(*GetMemory)(int size); // allocate from Zone + void (*FreeMemory)(void *ptr); // free memory from Zone + int (*AvailableMemory)(void); // available Zone memory + void *(*HunkAlloc)(int size); // allocate from hunk + //file system access + int (*FS_FOpenFile)( const char *qpath, fileHandle_t *file, fsMode_t mode ); + int (*FS_Read)( void *buffer, int len, fileHandle_t f ); + int (*FS_Write)( const void *buffer, int len, fileHandle_t f ); + void (*FS_FCloseFile)( fileHandle_t f ); + int (*FS_Seek)( fileHandle_t f, long offset, int origin ); + //debug visualisation stuff + int (*DebugLineCreate)(void); + void (*DebugLineDelete)(int line); + void (*DebugLineShow)(int line, vec3_t start, vec3_t end, int color); + // + int (*DebugPolygonCreate)(int color, int numPoints, vec3_t *points); + void (*DebugPolygonDelete)(int id); +} botlib_import_t; + +typedef struct aas_export_s +{ + //----------------------------------- + // be_aas_entity.h + //----------------------------------- + void (*AAS_EntityInfo)(int entnum, struct aas_entityinfo_s *info); + //----------------------------------- + // be_aas_main.h + //----------------------------------- + int (*AAS_Initialized)(void); + void (*AAS_PresenceTypeBoundingBox)(int presencetype, vec3_t mins, vec3_t maxs); + float (*AAS_Time)(void); + //-------------------------------------------- + // be_aas_sample.c + //-------------------------------------------- + int (*AAS_PointAreaNum)(vec3_t point); + int (*AAS_PointReachabilityAreaIndex)( vec3_t point ); + int (*AAS_TraceAreas)(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas); + int (*AAS_BBoxAreas)(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas); + int (*AAS_AreaInfo)( int areanum, struct aas_areainfo_s *info ); + //-------------------------------------------- + // be_aas_bspq3.c + //-------------------------------------------- + int (*AAS_PointContents)(vec3_t point); + int (*AAS_NextBSPEntity)(int ent); + int (*AAS_ValueForBSPEpairKey)(int ent, char *key, char *value, int size); + int (*AAS_VectorForBSPEpairKey)(int ent, char *key, vec3_t v); + int (*AAS_FloatForBSPEpairKey)(int ent, char *key, float *value); + int (*AAS_IntForBSPEpairKey)(int ent, char *key, int *value); + //-------------------------------------------- + // be_aas_reach.c + //-------------------------------------------- + int (*AAS_AreaReachability)(int areanum); + //-------------------------------------------- + // be_aas_route.c + //-------------------------------------------- + int (*AAS_AreaTravelTimeToGoalArea)(int areanum, vec3_t origin, int goalareanum, int travelflags); + int (*AAS_EnableRoutingArea)(int areanum, int enable); + int (*AAS_PredictRoute)(struct aas_predictroute_s *route, int areanum, vec3_t origin, + int goalareanum, int travelflags, int maxareas, int maxtime, + int stopevent, int stopcontents, int stoptfl, int stopareanum); + //-------------------------------------------- + // be_aas_altroute.c + //-------------------------------------------- + int (*AAS_AlternativeRouteGoals)(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, + struct aas_altroutegoal_s *altroutegoals, int maxaltroutegoals, + int type); + //-------------------------------------------- + // be_aas_move.c + //-------------------------------------------- + int (*AAS_Swimming)(vec3_t origin); + int (*AAS_PredictClientMovement)(struct aas_clientmove_s *move, + int entnum, vec3_t origin, + int presencetype, int onground, + vec3_t velocity, vec3_t cmdmove, + int cmdframes, + int maxframes, float frametime, + int stopevent, int stopareanum, int visualize); +} aas_export_t; + +typedef struct ea_export_s +{ + //ClientCommand elementary actions + void (*EA_Command)(int client, char *command ); + void (*EA_Say)(int client, char *str); + void (*EA_SayTeam)(int client, char *str); + // + void (*EA_Action)(int client, int action); + void (*EA_Gesture)(int client); + void (*EA_Talk)(int client); + void (*EA_Attack)(int client); + void (*EA_Use)(int client); + void (*EA_Respawn)(int client); + void (*EA_MoveUp)(int client); + void (*EA_MoveDown)(int client); + void (*EA_MoveForward)(int client); + void (*EA_MoveBack)(int client); + void (*EA_MoveLeft)(int client); + void (*EA_MoveRight)(int client); + void (*EA_Crouch)(int client); + + void (*EA_SelectWeapon)(int client, int weapon); + void (*EA_Jump)(int client); + void (*EA_DelayedJump)(int client); + void (*EA_Move)(int client, vec3_t dir, float speed); + void (*EA_View)(int client, vec3_t viewangles); + //send regular input to the server + void (*EA_EndRegular)(int client, float thinktime); + void (*EA_GetInput)(int client, float thinktime, bot_input_t *input); + void (*EA_ResetInput)(int client); +} ea_export_t; + +typedef struct ai_export_s +{ + //----------------------------------- + // be_ai_char.h + //----------------------------------- + int (*BotLoadCharacter)(char *charfile, float skill); + void (*BotFreeCharacter)(int character); + float (*Characteristic_Float)(int character, int index); + float (*Characteristic_BFloat)(int character, int index, float min, float max); + int (*Characteristic_Integer)(int character, int index); + int (*Characteristic_BInteger)(int character, int index, int min, int max); + void (*Characteristic_String)(int character, int index, char *buf, int size); + //----------------------------------- + // be_ai_chat.h + //----------------------------------- + int (*BotAllocChatState)(void); + void (*BotFreeChatState)(int handle); + void (*BotQueueConsoleMessage)(int chatstate, int type, char *message); + void (*BotRemoveConsoleMessage)(int chatstate, int handle); + int (*BotNextConsoleMessage)(int chatstate, struct bot_consolemessage_s *cm); + int (*BotNumConsoleMessages)(int chatstate); + void (*BotInitialChat)(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); + int (*BotNumInitialChats)(int chatstate, char *type); + int (*BotReplyChat)(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); + int (*BotChatLength)(int chatstate); + void (*BotEnterChat)(int chatstate, int client, int sendto); + void (*BotGetChatMessage)(int chatstate, char *buf, int size); + int (*StringContains)(char *str1, char *str2, int casesensitive); + int (*BotFindMatch)(char *str, struct bot_match_s *match, unsigned long int context); + void (*BotMatchVariable)(struct bot_match_s *match, int variable, char *buf, int size); + void (*UnifyWhiteSpaces)(char *string); + void (*BotReplaceSynonyms)(char *string, unsigned long int context); + int (*BotLoadChatFile)(int chatstate, char *chatfile, char *chatname); + void (*BotSetChatGender)(int chatstate, int gender); + void (*BotSetChatName)(int chatstate, char *name, int client); + //----------------------------------- + // be_ai_goal.h + //----------------------------------- + void (*BotResetGoalState)(int goalstate); + void (*BotResetAvoidGoals)(int goalstate); + void (*BotRemoveFromAvoidGoals)(int goalstate, int number); + void (*BotPushGoal)(int goalstate, struct bot_goal_s *goal); + void (*BotPopGoal)(int goalstate); + void (*BotEmptyGoalStack)(int goalstate); + void (*BotDumpAvoidGoals)(int goalstate); + void (*BotDumpGoalStack)(int goalstate); + void (*BotGoalName)(int number, char *name, int size); + int (*BotGetTopGoal)(int goalstate, struct bot_goal_s *goal); + int (*BotGetSecondGoal)(int goalstate, struct bot_goal_s *goal); + int (*BotChooseLTGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags); + int (*BotChooseNBGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags, + struct bot_goal_s *ltg, float maxtime); + int (*BotTouchingGoal)(vec3_t origin, struct bot_goal_s *goal); + int (*BotItemGoalInVisButNotVisible)(int viewer, vec3_t eye, vec3_t viewangles, struct bot_goal_s *goal); + int (*BotGetLevelItemGoal)(int index, char *classname, struct bot_goal_s *goal); + int (*BotGetNextCampSpotGoal)(int num, struct bot_goal_s *goal); + int (*BotGetMapLocationGoal)(char *name, struct bot_goal_s *goal); + float (*BotAvoidGoalTime)(int goalstate, int number); + void (*BotSetAvoidGoalTime)(int goalstate, int number, float avoidtime); + void (*BotInitLevelItems)(void); + void (*BotUpdateEntityItems)(void); + int (*BotLoadItemWeights)(int goalstate, char *filename); + void (*BotFreeItemWeights)(int goalstate); + void (*BotInterbreedGoalFuzzyLogic)(int parent1, int parent2, int child); + void (*BotSaveGoalFuzzyLogic)(int goalstate, char *filename); + void (*BotMutateGoalFuzzyLogic)(int goalstate, float range); + int (*BotAllocGoalState)(int client); + void (*BotFreeGoalState)(int handle); + //----------------------------------- + // be_ai_move.h + //----------------------------------- + void (*BotResetMoveState)(int movestate); + void (*BotMoveToGoal)(struct bot_moveresult_s *result, int movestate, struct bot_goal_s *goal, int travelflags); + int (*BotMoveInDirection)(int movestate, vec3_t dir, float speed, int type); + void (*BotResetAvoidReach)(int movestate); + void (*BotResetLastAvoidReach)(int movestate); + int (*BotReachabilityArea)(vec3_t origin, int testground); + int (*BotMovementViewTarget)(int movestate, struct bot_goal_s *goal, int travelflags, float lookahead, vec3_t target); + int (*BotPredictVisiblePosition)(vec3_t origin, int areanum, struct bot_goal_s *goal, int travelflags, vec3_t target); + int (*BotAllocMoveState)(void); + void (*BotFreeMoveState)(int handle); + void (*BotInitMoveState)(int handle, struct bot_initmove_s *initmove); + void (*BotAddAvoidSpot)(int movestate, vec3_t origin, float radius, int type); + //----------------------------------- + // be_ai_weap.h + //----------------------------------- + int (*BotChooseBestFightWeapon)(int weaponstate, int *inventory); + void (*BotGetWeaponInfo)(int weaponstate, int weapon, struct weaponinfo_s *weaponinfo); + int (*BotLoadWeaponWeights)(int weaponstate, char *filename); + int (*BotAllocWeaponState)(void); + void (*BotFreeWeaponState)(int weaponstate); + void (*BotResetWeaponState)(int weaponstate); + //----------------------------------- + // be_ai_gen.h + //----------------------------------- + int (*GeneticParentsAndChildSelection)(int numranks, float *ranks, int *parent1, int *parent2, int *child); +} ai_export_t; + +//bot AI library imported functions +typedef struct botlib_export_s +{ + //Area Awareness System functions + aas_export_t aas; + //Elementary Action functions + ea_export_t ea; + //AI functions + ai_export_t ai; + //setup the bot library, returns BLERR_ + int (*BotLibSetup)(void); + //shutdown the bot library, returns BLERR_ + int (*BotLibShutdown)(void); + //sets a library variable returns BLERR_ + int (*BotLibVarSet)(char *var_name, char *value); + //gets a library variable returns BLERR_ + int (*BotLibVarGet)(char *var_name, char *value, int size); + + //sets a C-like define returns BLERR_ + int (*PC_AddGlobalDefine)(char *string); + int (*PC_LoadSourceHandle)(const char *filename); + int (*PC_FreeSourceHandle)(int handle); + int (*PC_ReadTokenHandle)(int handle, pc_token_t *pc_token); + int (*PC_SourceFileAndLine)(int handle, char *filename, int *line); + + //start a frame in the bot library + int (*BotLibStartFrame)(float time); + //load a new map in the bot library + int (*BotLibLoadMap)(const char *mapname); + //entity updates + int (*BotLibUpdateEntity)(int ent, bot_entitystate_t *state); + //just for testing + int (*Test)(int parm0, char *parm1, vec3_t parm2, vec3_t parm3); +} botlib_export_t; + +//linking of bot library +botlib_export_t *GetBotLibAPI( int apiVersion, botlib_import_t *import ); + +/* Library variables: + +name: default: module(s): description: + +"basedir" "" l_utils.c base directory +"gamedir" "" l_utils.c game directory +"cddir" "" l_utils.c CD directory + +"log" "0" l_log.c enable/disable creating a log file +"maxclients" "4" be_interface.c maximum number of clients +"maxentities" "1024" be_interface.c maximum number of entities +"bot_developer" "0" be_interface.c bot developer mode + +"phys_friction" "6" be_aas_move.c ground friction +"phys_stopspeed" "100" be_aas_move.c stop speed +"phys_gravity" "800" be_aas_move.c gravity value +"phys_waterfriction" "1" be_aas_move.c water friction +"phys_watergravity" "400" be_aas_move.c gravity in water +"phys_maxvelocity" "320" be_aas_move.c maximum velocity +"phys_maxwalkvelocity" "320" be_aas_move.c maximum walk velocity +"phys_maxcrouchvelocity" "100" be_aas_move.c maximum crouch velocity +"phys_maxswimvelocity" "150" be_aas_move.c maximum swim velocity +"phys_walkaccelerate" "10" be_aas_move.c walk acceleration +"phys_airaccelerate" "1" be_aas_move.c air acceleration +"phys_swimaccelerate" "4" be_aas_move.c swim acceleration +"phys_maxstep" "18" be_aas_move.c maximum step height +"phys_maxsteepness" "0.7" be_aas_move.c maximum floor steepness +"phys_maxbarrier" "32" be_aas_move.c maximum barrier height +"phys_maxwaterjump" "19" be_aas_move.c maximum waterjump height +"phys_jumpvel" "270" be_aas_move.c jump z velocity +"phys_falldelta5" "40" be_aas_move.c +"phys_falldelta10" "60" be_aas_move.c +"rs_waterjump" "400" be_aas_move.c +"rs_teleport" "50" be_aas_move.c +"rs_barrierjump" "100" be_aas_move.c +"rs_startcrouch" "300" be_aas_move.c +"rs_startgrapple" "500" be_aas_move.c +"rs_startwalkoffledge" "70" be_aas_move.c +"rs_startjump" "300" be_aas_move.c +"rs_rocketjump" "500" be_aas_move.c +"rs_bfgjump" "500" be_aas_move.c +"rs_jumppad" "250" be_aas_move.c +"rs_aircontrolledjumppad" "300" be_aas_move.c +"rs_funcbob" "300" be_aas_move.c +"rs_startelevator" "50" be_aas_move.c +"rs_falldamage5" "300" be_aas_move.c +"rs_falldamage10" "500" be_aas_move.c +"rs_maxjumpfallheight" "450" be_aas_move.c + +"max_aaslinks" "4096" be_aas_sample.c maximum links in the AAS +"max_routingcache" "4096" be_aas_route.c maximum routing cache size in KB +"forceclustering" "0" be_aas_main.c force recalculation of clusters +"forcereachability" "0" be_aas_main.c force recalculation of reachabilities +"forcewrite" "0" be_aas_main.c force writing of aas file +"aasoptimize" "0" be_aas_main.c enable aas optimization +"sv_mapChecksum" "0" be_aas_main.c BSP file checksum +"bot_visualizejumppads" "0" be_aas_reach.c visualize jump pads + +"bot_reloadcharacters" "0" - reload bot character files +"ai_gametype" "0" be_ai_goal.c game type +"droppedweight" "1000" be_ai_goal.c additional dropped item weight +"weapindex_rocketlauncher" "5" be_ai_move.c rl weapon index for rocket jumping +"weapindex_bfg10k" "9" be_ai_move.c bfg weapon index for bfg jumping +"weapindex_grapple" "10" be_ai_move.c grapple weapon index for grappling +"entitytypemissile" "3" be_ai_move.c ET_MISSILE +"offhandgrapple" "0" be_ai_move.c enable off hand grapple hook +"cmd_grappleon" "grappleon" be_ai_move.c command to activate off hand grapple +"cmd_grappleoff" "grappleoff" be_ai_move.c command to deactivate off hand grapple +"itemconfig" "items.c" be_ai_goal.c item configuration file +"weaponconfig" "weapons.c" be_ai_weap.c weapon configuration file +"synfile" "syn.c" be_ai_chat.c file with synonyms +"rndfile" "rnd.c" be_ai_chat.c file with random strings +"matchfile" "match.c" be_ai_chat.c file with match strings +"nochat" "0" be_ai_chat.c disable chats +"max_messages" "1024" be_ai_chat.c console message heap size +"max_weaponinfo" "32" be_ai_weap.c maximum number of weapon info +"max_projectileinfo" "32" be_ai_weap.c maximum number of projectile info +"max_iteminfo" "256" be_ai_goal.c maximum number of item info +"max_levelitems" "256" be_ai_goal.c maximum number of level items + +*/ + diff --git a/reaction/engine/code/botlib/l_crc.c b/reaction/engine/code/botlib/l_crc.c new file mode 100644 index 00000000..e27b146e --- /dev/null +++ b/reaction/engine/code/botlib/l_crc.c @@ -0,0 +1,151 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_crc.c + * + * desc: CRC calculation + * + * $Archive: /MissionPack/CODE/botlib/l_crc.c $ + * + *****************************************************************************/ + +#include +#include +#include + +#include "../qcommon/q_shared.h" +#include "botlib.h" +#include "be_interface.h" //for botimport.Print + + +// FIXME: byte swap? + +// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 +// and the initial and final xor values shown below... in other words, the +// CCITT standard CRC used by XMODEM + +#define CRC_INIT_VALUE 0xffff +#define CRC_XOR_VALUE 0x0000 + +unsigned short crctable[257] = +{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CRC_Init(unsigned short *crcvalue) +{ + *crcvalue = CRC_INIT_VALUE; +} //end of the function CRC_Init +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CRC_ProcessByte(unsigned short *crcvalue, byte data) +{ + *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; +} //end of the function CRC_ProcessByte +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +unsigned short CRC_Value(unsigned short crcvalue) +{ + return crcvalue ^ CRC_XOR_VALUE; +} //end of the function CRC_Value +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +unsigned short CRC_ProcessString(unsigned char *data, int length) +{ + unsigned short crcvalue; + int i, ind; + + CRC_Init(&crcvalue); + + for (i = 0; i < length; i++) + { + ind = (crcvalue >> 8) ^ data[i]; + if (ind < 0 || ind > 256) ind = 0; + crcvalue = (crcvalue << 8) ^ crctable[ind]; + } //end for + return CRC_Value(crcvalue); +} //end of the function CRC_ProcessString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CRC_ContinueProcessString(unsigned short *crc, char *data, int length) +{ + int i; + + for (i = 0; i < length; i++) + { + *crc = (*crc << 8) ^ crctable[(*crc >> 8) ^ data[i]]; + } //end for +} //end of the function CRC_ProcessString diff --git a/reaction/engine/code/botlib/l_crc.h b/reaction/engine/code/botlib/l_crc.h new file mode 100644 index 00000000..f9c7e379 --- /dev/null +++ b/reaction/engine/code/botlib/l_crc.h @@ -0,0 +1,29 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +typedef unsigned short crc_t; + +void CRC_Init(unsigned short *crcvalue); +void CRC_ProcessByte(unsigned short *crcvalue, byte data); +unsigned short CRC_Value(unsigned short crcvalue); +unsigned short CRC_ProcessString(unsigned char *data, int length); +void CRC_ContinueProcessString(unsigned short *crc, char *data, int length); diff --git a/reaction/engine/code/botlib/l_libvar.c b/reaction/engine/code/botlib/l_libvar.c new file mode 100644 index 00000000..0270781f --- /dev/null +++ b/reaction/engine/code/botlib/l_libvar.c @@ -0,0 +1,295 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_libvar.c + * + * desc: bot library variables + * + * $Archive: /MissionPack/code/botlib/l_libvar.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_libvar.h" + +//list with library variables +libvar_t *libvarlist = NULL; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float LibVarStringValue(char *string) +{ + int dotfound = 0; + float value = 0; + + while(*string) + { + if (*string < '0' || *string > '9') + { + if (dotfound || *string != '.') + { + return 0; + } //end if + else + { + dotfound = 10; + string++; + } //end if + } //end if + if (dotfound) + { + value = value + (float) (*string - '0') / (float) dotfound; + dotfound *= 10; + } //end if + else + { + value = value * 10.0 + (float) (*string - '0'); + } //end else + string++; + } //end while + return value; +} //end of the function LibVarStringValue +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +libvar_t *LibVarAlloc(char *var_name) +{ + libvar_t *v; + + v = (libvar_t *) GetMemory(sizeof(libvar_t)); + Com_Memset(v, 0, sizeof(libvar_t)); + v->name = (char *) GetMemory(strlen(var_name)+1); + strcpy(v->name, var_name); + //add the variable in the list + v->next = libvarlist; + libvarlist = v; + return v; +} //end of the function LibVarAlloc +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void LibVarDeAlloc(libvar_t *v) +{ + if (v->string) FreeMemory(v->string); + FreeMemory(v->name); + FreeMemory(v); +} //end of the function LibVarDeAlloc +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void LibVarDeAllocAll(void) +{ + libvar_t *v; + + for (v = libvarlist; v; v = libvarlist) + { + libvarlist = libvarlist->next; + LibVarDeAlloc(v); + } //end for + libvarlist = NULL; +} //end of the function LibVarDeAllocAll +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +libvar_t *LibVarGet(char *var_name) +{ + libvar_t *v; + + for (v = libvarlist; v; v = v->next) + { + if (!Q_stricmp(v->name, var_name)) + { + return v; + } //end if + } //end for + return NULL; +} //end of the function LibVarGet +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *LibVarGetString(char *var_name) +{ + libvar_t *v; + + v = LibVarGet(var_name); + if (v) + { + return v->string; + } //end if + else + { + return ""; + } //end else +} //end of the function LibVarGetString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float LibVarGetValue(char *var_name) +{ + libvar_t *v; + + v = LibVarGet(var_name); + if (v) + { + return v->value; + } //end if + else + { + return 0; + } //end else +} //end of the function LibVarGetValue +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +libvar_t *LibVar(char *var_name, char *value) +{ + libvar_t *v; + v = LibVarGet(var_name); + if (v) return v; + //create new variable + v = LibVarAlloc(var_name); + //variable string + v->string = (char *) GetMemory(strlen(value) + 1); + strcpy(v->string, value); + //the value + v->value = LibVarStringValue(v->string); + //variable is modified + v->modified = qtrue; + // + return v; +} //end of the function LibVar +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *LibVarString(char *var_name, char *value) +{ + libvar_t *v; + + v = LibVar(var_name, value); + return v->string; +} //end of the function LibVarString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float LibVarValue(char *var_name, char *value) +{ + libvar_t *v; + + v = LibVar(var_name, value); + return v->value; +} //end of the function LibVarValue +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void LibVarSet(char *var_name, char *value) +{ + libvar_t *v; + + v = LibVarGet(var_name); + if (v) + { + FreeMemory(v->string); + } //end if + else + { + v = LibVarAlloc(var_name); + } //end else + //variable string + v->string = (char *) GetMemory(strlen(value) + 1); + strcpy(v->string, value); + //the value + v->value = LibVarStringValue(v->string); + //variable is modified + v->modified = qtrue; +} //end of the function LibVarSet +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean LibVarChanged(char *var_name) +{ + libvar_t *v; + + v = LibVarGet(var_name); + if (v) + { + return v->modified; + } //end if + else + { + return qfalse; + } //end else +} //end of the function LibVarChanged +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void LibVarSetNotModified(char *var_name) +{ + libvar_t *v; + + v = LibVarGet(var_name); + if (v) + { + v->modified = qfalse; + } //end if +} //end of the function LibVarSetNotModified diff --git a/reaction/engine/code/botlib/l_libvar.h b/reaction/engine/code/botlib/l_libvar.h new file mode 100644 index 00000000..d96685f4 --- /dev/null +++ b/reaction/engine/code/botlib/l_libvar.h @@ -0,0 +1,63 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_libvar.h + * + * desc: botlib vars + * + * $Archive: /source/code/botlib/l_libvar.h $ + * + *****************************************************************************/ + +//library variable +typedef struct libvar_s +{ + char *name; + char *string; + int flags; + qboolean modified; // set each time the cvar is changed + float value; + struct libvar_s *next; +} libvar_t; + +//removes all library variables +void LibVarDeAllocAll(void); +//gets the library variable with the given name +libvar_t *LibVarGet(char *var_name); +//gets the string of the library variable with the given name +char *LibVarGetString(char *var_name); +//gets the value of the library variable with the given name +float LibVarGetValue(char *var_name); +//creates the library variable if not existing already and returns it +libvar_t *LibVar(char *var_name, char *value); +//creates the library variable if not existing already and returns the value +float LibVarValue(char *var_name, char *value); +//creates the library variable if not existing already and returns the value string +char *LibVarString(char *var_name, char *value); +//sets the library variable +void LibVarSet(char *var_name, char *value); +//returns true if the library variable has been modified +qboolean LibVarChanged(char *var_name); +//sets the library variable to unmodified +void LibVarSetNotModified(char *var_name); + diff --git a/reaction/engine/code/botlib/l_log.c b/reaction/engine/code/botlib/l_log.c new file mode 100644 index 00000000..1b41b500 --- /dev/null +++ b/reaction/engine/code/botlib/l_log.c @@ -0,0 +1,169 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_log.c + * + * desc: log file + * + * $Archive: /MissionPack/CODE/botlib/l_log.c $ + * + *****************************************************************************/ + +#include +#include +#include + +#include "../qcommon/q_shared.h" +#include "botlib.h" +#include "be_interface.h" //for botimport.Print +#include "l_libvar.h" + +#define MAX_LOGFILENAMESIZE 1024 + +typedef struct logfile_s +{ + char filename[MAX_LOGFILENAMESIZE]; + FILE *fp; + int numwrites; +} logfile_t; + +static logfile_t logfile; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Open(char *filename) +{ + if (!LibVarValue("log", "0")) return; + if (!filename || !strlen(filename)) + { + botimport.Print(PRT_MESSAGE, "openlog \n"); + return; + } //end if + if (logfile.fp) + { + botimport.Print(PRT_ERROR, "log file %s is already opened\n", logfile.filename); + return; + } //end if + logfile.fp = fopen(filename, "wb"); + if (!logfile.fp) + { + botimport.Print(PRT_ERROR, "can't open the log file %s\n", filename); + return; + } //end if + strncpy(logfile.filename, filename, MAX_LOGFILENAMESIZE); + botimport.Print(PRT_MESSAGE, "Opened log %s\n", logfile.filename); +} //end of the function Log_Create +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Close(void) +{ + if (!logfile.fp) return; + if (fclose(logfile.fp)) + { + botimport.Print(PRT_ERROR, "can't close log file %s\n", logfile.filename); + return; + } //end if + logfile.fp = NULL; + botimport.Print(PRT_MESSAGE, "Closed log %s\n", logfile.filename); +} //end of the function Log_Close +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Shutdown(void) +{ + if (logfile.fp) Log_Close(); +} //end of the function Log_Shutdown +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void QDECL Log_Write(char *fmt, ...) +{ + va_list ap; + + if (!logfile.fp) return; + va_start(ap, fmt); + vfprintf(logfile.fp, fmt, ap); + va_end(ap); + //fprintf(logfile.fp, "\r\n"); + fflush(logfile.fp); +} //end of the function Log_Write +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void QDECL Log_WriteTimeStamped(char *fmt, ...) +{ + va_list ap; + + if (!logfile.fp) return; + fprintf(logfile.fp, "%d %02d:%02d:%02d:%02d ", + logfile.numwrites, + (int) (botlibglobals.time / 60 / 60), + (int) (botlibglobals.time / 60), + (int) (botlibglobals.time), + (int) ((int) (botlibglobals.time * 100)) - + ((int) botlibglobals.time) * 100); + va_start(ap, fmt); + vfprintf(logfile.fp, fmt, ap); + va_end(ap); + fprintf(logfile.fp, "\r\n"); + logfile.numwrites++; + fflush(logfile.fp); +} //end of the function Log_Write +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +FILE *Log_FilePointer(void) +{ + return logfile.fp; +} //end of the function Log_FilePointer +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Flush(void) +{ + if (logfile.fp) fflush(logfile.fp); +} //end of the function Log_Flush + diff --git a/reaction/engine/code/botlib/l_log.h b/reaction/engine/code/botlib/l_log.h new file mode 100644 index 00000000..3ddb4641 --- /dev/null +++ b/reaction/engine/code/botlib/l_log.h @@ -0,0 +1,46 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_log.h + * + * desc: log file + * + * $Archive: /source/code/botlib/l_log.h $ + * + *****************************************************************************/ + +//open a log file +void Log_Open(char *filename); +//close the current log file +void Log_Close(void); +//close log file if present +void Log_Shutdown(void); +//write to the current opened log file +void QDECL Log_Write(char *fmt, ...); +//write to the current opened log file with a time stamp +void QDECL Log_WriteTimeStamped(char *fmt, ...); +//returns a pointer to the log file +FILE *Log_FilePointer(void); +//flush log file +void Log_Flush(void); + diff --git a/reaction/engine/code/botlib/l_memory.c b/reaction/engine/code/botlib/l_memory.c new file mode 100644 index 00000000..da063ab0 --- /dev/null +++ b/reaction/engine/code/botlib/l_memory.c @@ -0,0 +1,463 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_memory.c + * + * desc: memory allocation + * + * $Archive: /MissionPack/code/botlib/l_memory.c $ + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "botlib.h" +#include "l_log.h" +#include "be_interface.h" + +//#define MEMDEBUG +//#define MEMORYMANEGER + +#define MEM_ID 0x12345678l +#define HUNK_ID 0x87654321l + +int allocatedmemory; +int totalmemorysize; +int numblocks; + +#ifdef MEMORYMANEGER + +typedef struct memoryblock_s +{ + unsigned long int id; + void *ptr; + int size; +#ifdef MEMDEBUG + char *label; + char *file; + int line; +#endif //MEMDEBUG + struct memoryblock_s *prev, *next; +} memoryblock_t; + +memoryblock_t *memory; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void LinkMemoryBlock(memoryblock_t *block) +{ + block->prev = NULL; + block->next = memory; + if (memory) memory->prev = block; + memory = block; +} //end of the function LinkMemoryBlock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void UnlinkMemoryBlock(memoryblock_t *block) +{ + if (block->prev) block->prev->next = block->next; + else memory = block->next; + if (block->next) block->next->prev = block->prev; +} //end of the function UnlinkMemoryBlock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; + memoryblock_t *block; + assert(botimport.GetMemory); + ptr = botimport.GetMemory(size + sizeof(memoryblock_t)); + block = (memoryblock_t *) ptr; + block->id = MEM_ID; + block->ptr = (char *) ptr + sizeof(memoryblock_t); + block->size = size + sizeof(memoryblock_t); +#ifdef MEMDEBUG + block->label = label; + block->file = file; + block->line = line; +#endif //MEMDEBUG + LinkMemoryBlock(block); + allocatedmemory += block->size; + totalmemorysize += block->size + sizeof(memoryblock_t); + numblocks++; + return block->ptr; +} //end of the function GetMemoryDebug +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetClearedMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; +#ifdef MEMDEBUG + ptr = GetMemoryDebug(size, label, file, line); +#else + ptr = GetMemory(size); +#endif //MEMDEBUG + Com_Memset(ptr, 0, size); + return ptr; +} //end of the function GetClearedMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetHunkMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; + memoryblock_t *block; + + ptr = botimport.HunkAlloc(size + sizeof(memoryblock_t)); + block = (memoryblock_t *) ptr; + block->id = HUNK_ID; + block->ptr = (char *) ptr + sizeof(memoryblock_t); + block->size = size + sizeof(memoryblock_t); +#ifdef MEMDEBUG + block->label = label; + block->file = file; + block->line = line; +#endif //MEMDEBUG + LinkMemoryBlock(block); + allocatedmemory += block->size; + totalmemorysize += block->size + sizeof(memoryblock_t); + numblocks++; + return block->ptr; +} //end of the function GetHunkMemoryDebug +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetClearedHunkMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; +#ifdef MEMDEBUG + ptr = GetHunkMemoryDebug(size, label, file, line); +#else + ptr = GetHunkMemory(size); +#endif //MEMDEBUG + Com_Memset(ptr, 0, size); + return ptr; +} //end of the function GetClearedHunkMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +memoryblock_t *BlockFromPointer(void *ptr, char *str) +{ + memoryblock_t *block; + + if (!ptr) + { +#ifdef MEMDEBUG + //char *crash = (char *) NULL; + //crash[0] = 1; + botimport.Print(PRT_FATAL, "%s: NULL pointer\n", str); +#endif // MEMDEBUG + return NULL; + } //end if + block = (memoryblock_t *) ((char *) ptr - sizeof(memoryblock_t)); + if (block->id != MEM_ID && block->id != HUNK_ID) + { + botimport.Print(PRT_FATAL, "%s: invalid memory block\n", str); + return NULL; + } //end if + if (block->ptr != ptr) + { + botimport.Print(PRT_FATAL, "%s: memory block pointer invalid\n", str); + return NULL; + } //end if + return block; +} //end of the function BlockFromPointer +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeMemory(void *ptr) +{ + memoryblock_t *block; + + block = BlockFromPointer(ptr, "FreeMemory"); + if (!block) return; + UnlinkMemoryBlock(block); + allocatedmemory -= block->size; + totalmemorysize -= block->size + sizeof(memoryblock_t); + numblocks--; + // + if (block->id == MEM_ID) + { + botimport.FreeMemory(block); + } //end if +} //end of the function FreeMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AvailableMemory(void) +{ + return botimport.AvailableMemory(); +} //end of the function AvailableMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int MemoryByteSize(void *ptr) +{ + memoryblock_t *block; + + block = BlockFromPointer(ptr, "MemoryByteSize"); + if (!block) return 0; + return block->size; +} //end of the function MemoryByteSize +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintUsedMemorySize(void) +{ + botimport.Print(PRT_MESSAGE, "total allocated memory: %d KB\n", allocatedmemory >> 10); + botimport.Print(PRT_MESSAGE, "total botlib memory: %d KB\n", totalmemorysize >> 10); + botimport.Print(PRT_MESSAGE, "total memory blocks: %d\n", numblocks); +} //end of the function PrintUsedMemorySize +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintMemoryLabels(void) +{ + memoryblock_t *block; + int i; + + PrintUsedMemorySize(); + i = 0; + Log_Write("============= Botlib memory log ==============\r\n"); + Log_Write("\r\n"); + for (block = memory; block; block = block->next) + { +#ifdef MEMDEBUG + if (block->id == HUNK_ID) + { + Log_Write("%6d, hunk %p, %8d: %24s line %6d: %s\r\n", i, block->ptr, block->size, block->file, block->line, block->label); + } //end if + else + { + Log_Write("%6d, %p, %8d: %24s line %6d: %s\r\n", i, block->ptr, block->size, block->file, block->line, block->label); + } //end else +#endif //MEMDEBUG + i++; + } //end for +} //end of the function PrintMemoryLabels +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void DumpMemory(void) +{ + memoryblock_t *block; + + for (block = memory; block; block = memory) + { + FreeMemory(block->ptr); + } //end for + totalmemorysize = 0; + allocatedmemory = 0; +} //end of the function DumpMemory + +#else + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; + unsigned long int *memid; + + ptr = botimport.GetMemory(size + sizeof(unsigned long int)); + if (!ptr) return NULL; + memid = (unsigned long int *) ptr; + *memid = MEM_ID; + return (unsigned long int *) ((char *) ptr + sizeof(unsigned long int)); +} //end of the function GetMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetClearedMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; +#ifdef MEMDEBUG + ptr = GetMemoryDebug(size, label, file, line); +#else + ptr = GetMemory(size); +#endif //MEMDEBUG + Com_Memset(ptr, 0, size); + return ptr; +} //end of the function GetClearedMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetHunkMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; + unsigned long int *memid; + + ptr = botimport.HunkAlloc(size + sizeof(unsigned long int)); + if (!ptr) return NULL; + memid = (unsigned long int *) ptr; + *memid = HUNK_ID; + return (unsigned long int *) ((char *) ptr + sizeof(unsigned long int)); +} //end of the function GetHunkMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetClearedHunkMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; +#ifdef MEMDEBUG + ptr = GetHunkMemoryDebug(size, label, file, line); +#else + ptr = GetHunkMemory(size); +#endif //MEMDEBUG + Com_Memset(ptr, 0, size); + return ptr; +} //end of the function GetClearedHunkMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeMemory(void *ptr) +{ + unsigned long int *memid; + + memid = (unsigned long int *) ((char *) ptr - sizeof(unsigned long int)); + + if (*memid == MEM_ID) + { + botimport.FreeMemory(memid); + } //end if +} //end of the function FreeMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AvailableMemory(void) +{ + return botimport.AvailableMemory(); +} //end of the function AvailableMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintUsedMemorySize(void) +{ +} //end of the function PrintUsedMemorySize +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintMemoryLabels(void) +{ +} //end of the function PrintMemoryLabels + +#endif diff --git a/reaction/engine/code/botlib/l_memory.h b/reaction/engine/code/botlib/l_memory.h new file mode 100644 index 00000000..17c89d5b --- /dev/null +++ b/reaction/engine/code/botlib/l_memory.h @@ -0,0 +1,76 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_memory.h + * + * desc: memory management + * + * $Archive: /source/code/botlib/l_memory.h $ + * + *****************************************************************************/ + +//#define MEMDEBUG + +#ifdef MEMDEBUG +#define GetMemory(size) GetMemoryDebug(size, #size, __FILE__, __LINE__); +#define GetClearedMemory(size) GetClearedMemoryDebug(size, #size, __FILE__, __LINE__); +//allocate a memory block of the given size +void *GetMemoryDebug(unsigned long size, char *label, char *file, int line); +//allocate a memory block of the given size and clear it +void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line); +// +#define GetHunkMemory(size) GetHunkMemoryDebug(size, #size, __FILE__, __LINE__); +#define GetClearedHunkMemory(size) GetClearedHunkMemoryDebug(size, #size, __FILE__, __LINE__); +//allocate a memory block of the given size +void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line); +//allocate a memory block of the given size and clear it +void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line); +#else +//allocate a memory block of the given size +void *GetMemory(unsigned long size); +//allocate a memory block of the given size and clear it +void *GetClearedMemory(unsigned long size); +// +#ifdef BSPC +#define GetHunkMemory GetMemory +#define GetClearedHunkMemory GetClearedMemory +#else +//allocate a memory block of the given size +void *GetHunkMemory(unsigned long size); +//allocate a memory block of the given size and clear it +void *GetClearedHunkMemory(unsigned long size); +#endif +#endif + +//free the given memory block +void FreeMemory(void *ptr); +//returns the amount available memory +int AvailableMemory(void); +//prints the total used memory size +void PrintUsedMemorySize(void); +//print all memory blocks with label +void PrintMemoryLabels(void); +//returns the size of the memory block in bytes +int MemoryByteSize(void *ptr); +//free all allocated memory +void DumpMemory(void); diff --git a/reaction/engine/code/botlib/l_precomp.c b/reaction/engine/code/botlib/l_precomp.c new file mode 100644 index 00000000..06e78776 --- /dev/null +++ b/reaction/engine/code/botlib/l_precomp.c @@ -0,0 +1,3230 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: l_precomp.c + * + * desc: pre compiler + * + * $Archive: /MissionPack/code/botlib/l_precomp.c $ + * + *****************************************************************************/ + +//Notes: fix: PC_StringizeTokens + +//#define SCREWUP +//#define BOTLIB +//#define QUAKE +//#define QUAKEC +//#define MEQCC + +#ifdef SCREWUP +#include +#include +#include +#include +#include +#include +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" + +typedef enum {qfalse, qtrue} qboolean; +#endif //SCREWUP + +#ifdef BOTLIB +#include "../qcommon/q_shared.h" +#include "botlib.h" +#include "be_interface.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_log.h" +#endif //BOTLIB + +#ifdef MEQCC +#include "qcc.h" +#include "time.h" //time & ctime +#include "math.h" //fabs +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_log.h" + +#define qtrue true +#define qfalse false +#endif //MEQCC + +#ifdef BSPC +//include files for usage in the BSP Converter +#include "../bspc/qbsp.h" +#include "../bspc/l_log.h" +#include "../bspc/l_mem.h" +#include "l_precomp.h" + +#define qtrue true +#define qfalse false +#define Q_stricmp stricmp + +#endif //BSPC + +#if defined(QUAKE) && !defined(BSPC) +#include "l_utils.h" +#endif //QUAKE + +//#define DEBUG_EVAL + +#define MAX_DEFINEPARMS 128 + +#define DEFINEHASHING 1 + +//directive name with parse function +typedef struct directive_s +{ + char *name; + int (*func)(source_t *source); +} directive_t; + +#define DEFINEHASHSIZE 1024 + +#define TOKEN_HEAP_SIZE 4096 + +int numtokens; +/* +int tokenheapinitialized; //true when the token heap is initialized +token_t token_heap[TOKEN_HEAP_SIZE]; //heap with tokens +token_t *freetokens; //free tokens from the heap +*/ + +//list with global defines added to every source loaded +define_t *globaldefines; + +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void QDECL SourceError(source_t *source, char *str, ...) +{ + char text[1024]; + va_list ap; + + va_start(ap, str); + Q_vsnprintf(text, sizeof(text), str, ap); + va_end(ap); +#ifdef BOTLIB + botimport.Print(PRT_ERROR, "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); +#endif //BOTLIB +#ifdef MEQCC + printf("error: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); +#endif //MEQCC +#ifdef BSPC + Log_Print("error: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); +#endif //BSPC +} //end of the function SourceError +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void QDECL SourceWarning(source_t *source, char *str, ...) +{ + char text[1024]; + va_list ap; + + va_start(ap, str); + Q_vsnprintf(text, sizeof(text), str, ap); + va_end(ap); +#ifdef BOTLIB + botimport.Print(PRT_WARNING, "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); +#endif //BOTLIB +#ifdef MEQCC + printf("warning: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); +#endif //MEQCC +#ifdef BSPC + Log_Print("warning: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); +#endif //BSPC +} //end of the function ScriptWarning +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_PushIndent(source_t *source, int type, int skip) +{ + indent_t *indent; + + indent = (indent_t *) GetMemory(sizeof(indent_t)); + indent->type = type; + indent->script = source->scriptstack; + indent->skip = (skip != 0); + source->skip += indent->skip; + indent->next = source->indentstack; + source->indentstack = indent; +} //end of the function PC_PushIndent +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_PopIndent(source_t *source, int *type, int *skip) +{ + indent_t *indent; + + *type = 0; + *skip = 0; + + indent = source->indentstack; + if (!indent) return; + + //must be an indent from the current script + if (source->indentstack->script != source->scriptstack) return; + + *type = indent->type; + *skip = indent->skip; + source->indentstack = source->indentstack->next; + source->skip -= indent->skip; + FreeMemory(indent); +} //end of the function PC_PopIndent +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_PushScript(source_t *source, script_t *script) +{ + script_t *s; + + for (s = source->scriptstack; s; s = s->next) + { + if (!Q_stricmp(s->filename, script->filename)) + { + SourceError(source, "%s recursively included", script->filename); + return; + } //end if + } //end for + //push the script on the script stack + script->next = source->scriptstack; + source->scriptstack = script; +} //end of the function PC_PushScript +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_InitTokenHeap(void) +{ + /* + int i; + + if (tokenheapinitialized) return; + freetokens = NULL; + for (i = 0; i < TOKEN_HEAP_SIZE; i++) + { + token_heap[i].next = freetokens; + freetokens = &token_heap[i]; + } //end for + tokenheapinitialized = qtrue; + */ +} //end of the function PC_InitTokenHeap +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +token_t *PC_CopyToken(token_t *token) +{ + token_t *t; + +// t = (token_t *) malloc(sizeof(token_t)); + t = (token_t *) GetMemory(sizeof(token_t)); +// t = freetokens; + if (!t) + { +#ifdef BSPC + Error("out of token space\n"); +#else + Com_Error(ERR_FATAL, "out of token space\n"); +#endif + return NULL; + } //end if +// freetokens = freetokens->next; + Com_Memcpy(t, token, sizeof(token_t)); + t->next = NULL; + numtokens++; + return t; +} //end of the function PC_CopyToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_FreeToken(token_t *token) +{ + //free(token); + FreeMemory(token); +// token->next = freetokens; +// freetokens = token; + numtokens--; +} //end of the function PC_FreeToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ReadSourceToken(source_t *source, token_t *token) +{ + token_t *t; + script_t *script; + int type, skip; + + //if there's no token already available + while(!source->tokens) + { + //if there's a token to read from the script + if (PS_ReadToken(source->scriptstack, token)) return qtrue; + //if at the end of the script + if (EndOfScript(source->scriptstack)) + { + //remove all indents of the script + while(source->indentstack && + source->indentstack->script == source->scriptstack) + { + SourceWarning(source, "missing #endif"); + PC_PopIndent(source, &type, &skip); + } //end if + } //end if + //if this was the initial script + if (!source->scriptstack->next) return qfalse; + //remove the script and return to the last one + script = source->scriptstack; + source->scriptstack = source->scriptstack->next; + FreeScript(script); + } //end while + //copy the already available token + Com_Memcpy(token, source->tokens, sizeof(token_t)); + //free the read token + t = source->tokens; + source->tokens = source->tokens->next; + PC_FreeToken(t); + return qtrue; +} //end of the function PC_ReadSourceToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_UnreadSourceToken(source_t *source, token_t *token) +{ + token_t *t; + + t = PC_CopyToken(token); + t->next = source->tokens; + source->tokens = t; + return qtrue; +} //end of the function PC_UnreadSourceToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ReadDefineParms(source_t *source, define_t *define, token_t **parms, int maxparms) +{ + token_t token, *t, *last; + int i, done, lastcomma, numparms, indent; + + if (!PC_ReadSourceToken(source, &token)) + { + SourceError(source, "define %s missing parms", define->name); + return qfalse; + } //end if + // + if (define->numparms > maxparms) + { + SourceError(source, "define with more than %d parameters", maxparms); + return qfalse; + } //end if + // + for (i = 0; i < define->numparms; i++) parms[i] = NULL; + //if no leading "(" + if (strcmp(token.string, "(")) + { + PC_UnreadSourceToken(source, &token); + SourceError(source, "define %s missing parms", define->name); + return qfalse; + } //end if + //read the define parameters + for (done = 0, numparms = 0, indent = 0; !done;) + { + if (numparms >= maxparms) + { + SourceError(source, "define %s with too many parms", define->name); + return qfalse; + } //end if + if (numparms >= define->numparms) + { + SourceWarning(source, "define %s has too many parms", define->name); + return qfalse; + } //end if + parms[numparms] = NULL; + lastcomma = 1; + last = NULL; + while(!done) + { + // + if (!PC_ReadSourceToken(source, &token)) + { + SourceError(source, "define %s incomplete", define->name); + return qfalse; + } //end if + // + if (!strcmp(token.string, ",")) + { + if (indent <= 0) + { + if (lastcomma) SourceWarning(source, "too many comma's"); + lastcomma = 1; + break; + } //end if + } //end if + lastcomma = 0; + // + if (!strcmp(token.string, "(")) + { + indent++; + continue; + } //end if + else if (!strcmp(token.string, ")")) + { + if (--indent <= 0) + { + if (!parms[define->numparms-1]) + { + SourceWarning(source, "too few define parms"); + } //end if + done = 1; + break; + } //end if + } //end if + // + if (numparms < define->numparms) + { + // + t = PC_CopyToken(&token); + t->next = NULL; + if (last) last->next = t; + else parms[numparms] = t; + last = t; + } //end if + } //end while + numparms++; + } //end for + return qtrue; +} //end of the function PC_ReadDefineParms +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_StringizeTokens(token_t *tokens, token_t *token) +{ + token_t *t; + + token->type = TT_STRING; + token->whitespace_p = NULL; + token->endwhitespace_p = NULL; + token->string[0] = '\0'; + strcat(token->string, "\""); + for (t = tokens; t; t = t->next) + { + strncat(token->string, t->string, MAX_TOKEN - strlen(token->string) - 1); + } //end for + strncat(token->string, "\"", MAX_TOKEN - strlen(token->string) - 1); + return qtrue; +} //end of the function PC_StringizeTokens +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_MergeTokens(token_t *t1, token_t *t2) +{ + //merging of a name with a name or number + if (t1->type == TT_NAME && (t2->type == TT_NAME || t2->type == TT_NUMBER)) + { + strcat(t1->string, t2->string); + return qtrue; + } //end if + //merging of two strings + if (t1->type == TT_STRING && t2->type == TT_STRING) + { + //remove trailing double quote + t1->string[strlen(t1->string)-1] = '\0'; + //concat without leading double quote + strcat(t1->string, &t2->string[1]); + return qtrue; + } //end if + //FIXME: merging of two number of the same sub type + return qfalse; +} //end of the function PC_MergeTokens +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +/* +void PC_PrintDefine(define_t *define) +{ + printf("define->name = %s\n", define->name); + printf("define->flags = %d\n", define->flags); + printf("define->builtin = %d\n", define->builtin); + printf("define->numparms = %d\n", define->numparms); +// token_t *parms; //define parameters +// token_t *tokens; //macro tokens (possibly containing parm tokens) +// struct define_s *next; //next defined macro in a list +} //end of the function PC_PrintDefine*/ +#if DEFINEHASHING +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_PrintDefineHashTable(define_t **definehash) +{ + int i; + define_t *d; + + for (i = 0; i < DEFINEHASHSIZE; i++) + { + Log_Write("%4d:", i); + for (d = definehash[i]; d; d = d->hashnext) + { + Log_Write(" %s", d->name); + } //end for + Log_Write("\n"); + } //end for +} //end of the function PC_PrintDefineHashTable +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +//char primes[16] = {1, 3, 5, 7, 11, 13, 17, 19, 23, 27, 29, 31, 37, 41, 43, 47}; + +int PC_NameHash(char *name) +{ + int register hash, i; + + hash = 0; + for (i = 0; name[i] != '\0'; i++) + { + hash += name[i] * (119 + i); + //hash += (name[i] << 7) + i; + //hash += (name[i] << (i&15)); + } //end while + hash = (hash ^ (hash >> 10) ^ (hash >> 20)) & (DEFINEHASHSIZE-1); + return hash; +} //end of the function PC_NameHash +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_AddDefineToHash(define_t *define, define_t **definehash) +{ + int hash; + + hash = PC_NameHash(define->name); + define->hashnext = definehash[hash]; + definehash[hash] = define; +} //end of the function PC_AddDefineToHash +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +define_t *PC_FindHashedDefine(define_t **definehash, char *name) +{ + define_t *d; + int hash; + + hash = PC_NameHash(name); + for (d = definehash[hash]; d; d = d->hashnext) + { + if (!strcmp(d->name, name)) return d; + } //end for + return NULL; +} //end of the function PC_FindHashedDefine +#endif //DEFINEHASHING +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +define_t *PC_FindDefine(define_t *defines, char *name) +{ + define_t *d; + + for (d = defines; d; d = d->next) + { + if (!strcmp(d->name, name)) return d; + } //end for + return NULL; +} //end of the function PC_FindDefine +//============================================================================ +// +// Parameter: - +// Returns: number of the parm +// if no parm found with the given name -1 is returned +// Changes Globals: - +//============================================================================ +int PC_FindDefineParm(define_t *define, char *name) +{ + token_t *p; + int i; + + i = 0; + for (p = define->parms; p; p = p->next) + { + if (!strcmp(p->string, name)) return i; + i++; + } //end for + return -1; +} //end of the function PC_FindDefineParm +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_FreeDefine(define_t *define) +{ + token_t *t, *next; + + //free the define parameters + for (t = define->parms; t; t = next) + { + next = t->next; + PC_FreeToken(t); + } //end for + //free the define tokens + for (t = define->tokens; t; t = next) + { + next = t->next; + PC_FreeToken(t); + } //end for + //free the define + FreeMemory(define->name); + FreeMemory(define); +} //end of the function PC_FreeDefine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_AddBuiltinDefines(source_t *source) +{ + int i; + define_t *define; + struct builtin + { + char *string; + int builtin; + } builtin[] = { + { "__LINE__", BUILTIN_LINE }, + { "__FILE__", BUILTIN_FILE }, + { "__DATE__", BUILTIN_DATE }, + { "__TIME__", BUILTIN_TIME }, +// { "__STDC__", BUILTIN_STDC }, + { NULL, 0 } + }; + + for (i = 0; builtin[i].string; i++) + { + define = (define_t *) GetMemory(sizeof(define_t)); + Com_Memset(define, 0, sizeof(define_t)); + define->name = (char *) GetMemory(strlen(builtin[i].string) + 1); + strcpy(define->name, builtin[i].string); + define->flags |= DEFINE_FIXED; + define->builtin = builtin[i].builtin; + //add the define to the source +#if DEFINEHASHING + PC_AddDefineToHash(define, source->definehash); +#else + define->next = source->defines; + source->defines = define; +#endif //DEFINEHASHING + } //end for +} //end of the function PC_AddBuiltinDefines +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpandBuiltinDefine(source_t *source, token_t *deftoken, define_t *define, + token_t **firsttoken, token_t **lasttoken) +{ + token_t *token; + time_t t; + + char *curtime; + + token = PC_CopyToken(deftoken); + switch(define->builtin) + { + case BUILTIN_LINE: + { + sprintf(token->string, "%d", deftoken->line); +#ifdef NUMBERVALUE + token->intvalue = deftoken->line; + token->floatvalue = deftoken->line; +#endif //NUMBERVALUE + token->type = TT_NUMBER; + token->subtype = TT_DECIMAL | TT_INTEGER; + *firsttoken = token; + *lasttoken = token; + break; + } //end case + case BUILTIN_FILE: + { + strcpy(token->string, source->scriptstack->filename); + token->type = TT_NAME; + token->subtype = strlen(token->string); + *firsttoken = token; + *lasttoken = token; + break; + } //end case + case BUILTIN_DATE: + { + t = time(NULL); + curtime = ctime(&t); + strcpy(token->string, "\""); + strncat(token->string, curtime+4, 7); + strncat(token->string+7, curtime+20, 4); + strcat(token->string, "\""); + free(curtime); + token->type = TT_NAME; + token->subtype = strlen(token->string); + *firsttoken = token; + *lasttoken = token; + break; + } //end case + case BUILTIN_TIME: + { + t = time(NULL); + curtime = ctime(&t); + strcpy(token->string, "\""); + strncat(token->string, curtime+11, 8); + strcat(token->string, "\""); + free(curtime); + token->type = TT_NAME; + token->subtype = strlen(token->string); + *firsttoken = token; + *lasttoken = token; + break; + } //end case + case BUILTIN_STDC: + default: + { + *firsttoken = NULL; + *lasttoken = NULL; + break; + } //end case + } //end switch + return qtrue; +} //end of the function PC_ExpandBuiltinDefine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpandDefine(source_t *source, token_t *deftoken, define_t *define, + token_t **firsttoken, token_t **lasttoken) +{ + token_t *parms[MAX_DEFINEPARMS], *dt, *pt, *t; + token_t *t1, *t2, *first, *last, *nextpt, token; + int parmnum, i; + + //if it is a builtin define + if (define->builtin) + { + return PC_ExpandBuiltinDefine(source, deftoken, define, firsttoken, lasttoken); + } //end if + //if the define has parameters + if (define->numparms) + { + if (!PC_ReadDefineParms(source, define, parms, MAX_DEFINEPARMS)) return qfalse; +#ifdef DEBUG_EVAL + for (i = 0; i < define->numparms; i++) + { + Log_Write("define parms %d:", i); + for (pt = parms[i]; pt; pt = pt->next) + { + Log_Write("%s", pt->string); + } //end for + } //end for +#endif //DEBUG_EVAL + } //end if + //empty list at first + first = NULL; + last = NULL; + //create a list with tokens of the expanded define + for (dt = define->tokens; dt; dt = dt->next) + { + parmnum = -1; + //if the token is a name, it could be a define parameter + if (dt->type == TT_NAME) + { + parmnum = PC_FindDefineParm(define, dt->string); + } //end if + //if it is a define parameter + if (parmnum >= 0) + { + for (pt = parms[parmnum]; pt; pt = pt->next) + { + t = PC_CopyToken(pt); + //add the token to the list + t->next = NULL; + if (last) last->next = t; + else first = t; + last = t; + } //end for + } //end if + else + { + //if stringizing operator + if (dt->string[0] == '#' && dt->string[1] == '\0') + { + //the stringizing operator must be followed by a define parameter + if (dt->next) parmnum = PC_FindDefineParm(define, dt->next->string); + else parmnum = -1; + // + if (parmnum >= 0) + { + //step over the stringizing operator + dt = dt->next; + //stringize the define parameter tokens + if (!PC_StringizeTokens(parms[parmnum], &token)) + { + SourceError(source, "can't stringize tokens"); + return qfalse; + } //end if + t = PC_CopyToken(&token); + } //end if + else + { + SourceWarning(source, "stringizing operator without define parameter"); + continue; + } //end if + } //end if + else + { + t = PC_CopyToken(dt); + } //end else + //add the token to the list + t->next = NULL; + if (last) last->next = t; + else first = t; + last = t; + } //end else + } //end for + //check for the merging operator + for (t = first; t; ) + { + if (t->next) + { + //if the merging operator + if (t->next->string[0] == '#' && t->next->string[1] == '#') + { + t1 = t; + t2 = t->next->next; + if (t2) + { + if (!PC_MergeTokens(t1, t2)) + { + SourceError(source, "can't merge %s with %s", t1->string, t2->string); + return qfalse; + } //end if + PC_FreeToken(t1->next); + t1->next = t2->next; + if (t2 == last) last = t1; + PC_FreeToken(t2); + continue; + } //end if + } //end if + } //end if + t = t->next; + } //end for + //store the first and last token of the list + *firsttoken = first; + *lasttoken = last; + //free all the parameter tokens + for (i = 0; i < define->numparms; i++) + { + for (pt = parms[i]; pt; pt = nextpt) + { + nextpt = pt->next; + PC_FreeToken(pt); + } //end for + } //end for + // + return qtrue; +} //end of the function PC_ExpandDefine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpandDefineIntoSource(source_t *source, token_t *deftoken, define_t *define) +{ + token_t *firsttoken, *lasttoken; + + if (!PC_ExpandDefine(source, deftoken, define, &firsttoken, &lasttoken)) return qfalse; + + if (firsttoken && lasttoken) + { + lasttoken->next = source->tokens; + source->tokens = firsttoken; + return qtrue; + } //end if + return qfalse; +} //end of the function PC_ExpandDefineIntoSource +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_ConvertPath(char *path) +{ + char *ptr; + + //remove double path seperators + for (ptr = path; *ptr;) + { + if ((*ptr == '\\' || *ptr == '/') && + (*(ptr+1) == '\\' || *(ptr+1) == '/')) + { + strcpy(ptr, ptr+1); + } //end if + else + { + ptr++; + } //end else + } //end while + //set OS dependent path seperators + for (ptr = path; *ptr;) + { + if (*ptr == '/' || *ptr == '\\') *ptr = PATHSEPERATOR_CHAR; + ptr++; + } //end while +} //end of the function PC_ConvertPath +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_include(source_t *source) +{ + script_t *script; + token_t token; + char path[MAX_PATH]; +#ifdef QUAKE + foundfile_t file; +#endif //QUAKE + + if (source->skip > 0) return qtrue; + // + if (!PC_ReadSourceToken(source, &token)) + { + SourceError(source, "#include without file name"); + return qfalse; + } //end if + if (token.linescrossed > 0) + { + SourceError(source, "#include without file name"); + return qfalse; + } //end if + if (token.type == TT_STRING) + { + StripDoubleQuotes(token.string); + PC_ConvertPath(token.string); + script = LoadScriptFile(token.string); + if (!script) + { + strcpy(path, source->includepath); + strcat(path, token.string); + script = LoadScriptFile(path); + } //end if + } //end if + else if (token.type == TT_PUNCTUATION && *token.string == '<') + { + strcpy(path, source->includepath); + while(PC_ReadSourceToken(source, &token)) + { + if (token.linescrossed > 0) + { + PC_UnreadSourceToken(source, &token); + break; + } //end if + if (token.type == TT_PUNCTUATION && *token.string == '>') break; + strncat(path, token.string, MAX_PATH - 1); + } //end while + if (*token.string != '>') + { + SourceWarning(source, "#include missing trailing >"); + } //end if + if (!strlen(path)) + { + SourceError(source, "#include without file name between < >"); + return qfalse; + } //end if + PC_ConvertPath(path); + script = LoadScriptFile(path); + } //end if + else + { + SourceError(source, "#include without file name"); + return qfalse; + } //end else +#ifdef QUAKE + if (!script) + { + Com_Memset(&file, 0, sizeof(foundfile_t)); + script = LoadScriptFile(path); + if (script) strncpy(script->filename, path, MAX_PATH); + } //end if +#endif //QUAKE + if (!script) + { +#ifdef SCREWUP + SourceWarning(source, "file %s not found", path); + return qtrue; +#else + SourceError(source, "file %s not found", path); + return qfalse; +#endif //SCREWUP + } //end if + PC_PushScript(source, script); + return qtrue; +} //end of the function PC_Directive_include +//============================================================================ +// reads a token from the current line, continues reading on the next +// line only if a backslash '\' is encountered. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ReadLine(source_t *source, token_t *token) +{ + int crossline; + + crossline = 0; + do + { + if (!PC_ReadSourceToken(source, token)) return qfalse; + + if (token->linescrossed > crossline) + { + PC_UnreadSourceToken(source, token); + return qfalse; + } //end if + crossline = 1; + } while(!strcmp(token->string, "\\")); + return qtrue; +} //end of the function PC_ReadLine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_WhiteSpaceBeforeToken(token_t *token) +{ + return token->endwhitespace_p - token->whitespace_p > 0; +} //end of the function PC_WhiteSpaceBeforeToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_ClearTokenWhiteSpace(token_t *token) +{ + token->whitespace_p = NULL; + token->endwhitespace_p = NULL; + token->linescrossed = 0; +} //end of the function PC_ClearTokenWhiteSpace +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_undef(source_t *source) +{ + token_t token; + define_t *define, *lastdefine; + int hash; + + if (source->skip > 0) return qtrue; + // + if (!PC_ReadLine(source, &token)) + { + SourceError(source, "undef without name"); + return qfalse; + } //end if + if (token.type != TT_NAME) + { + PC_UnreadSourceToken(source, &token); + SourceError(source, "expected name, found %s", token.string); + return qfalse; + } //end if +#if DEFINEHASHING + + hash = PC_NameHash(token.string); + for (lastdefine = NULL, define = source->definehash[hash]; define; define = define->hashnext) + { + if (!strcmp(define->name, token.string)) + { + if (define->flags & DEFINE_FIXED) + { + SourceWarning(source, "can't undef %s", token.string); + } //end if + else + { + if (lastdefine) lastdefine->hashnext = define->hashnext; + else source->definehash[hash] = define->hashnext; + PC_FreeDefine(define); + } //end else + break; + } //end if + lastdefine = define; + } //end for +#else //DEFINEHASHING + for (lastdefine = NULL, define = source->defines; define; define = define->next) + { + if (!strcmp(define->name, token.string)) + { + if (define->flags & DEFINE_FIXED) + { + SourceWarning(source, "can't undef %s", token.string); + } //end if + else + { + if (lastdefine) lastdefine->next = define->next; + else source->defines = define->next; + PC_FreeDefine(define); + } //end else + break; + } //end if + lastdefine = define; + } //end for +#endif //DEFINEHASHING + return qtrue; +} //end of the function PC_Directive_undef +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_define(source_t *source) +{ + token_t token, *t, *last; + define_t *define; + + if (source->skip > 0) return qtrue; + // + if (!PC_ReadLine(source, &token)) + { + SourceError(source, "#define without name"); + return qfalse; + } //end if + if (token.type != TT_NAME) + { + PC_UnreadSourceToken(source, &token); + SourceError(source, "expected name after #define, found %s", token.string); + return qfalse; + } //end if + //check if the define already exists +#if DEFINEHASHING + define = PC_FindHashedDefine(source->definehash, token.string); +#else + define = PC_FindDefine(source->defines, token.string); +#endif //DEFINEHASHING + if (define) + { + if (define->flags & DEFINE_FIXED) + { + SourceError(source, "can't redefine %s", token.string); + return qfalse; + } //end if + SourceWarning(source, "redefinition of %s", token.string); + //unread the define name before executing the #undef directive + PC_UnreadSourceToken(source, &token); + if (!PC_Directive_undef(source)) return qfalse; + //if the define was not removed (define->flags & DEFINE_FIXED) +#if DEFINEHASHING + define = PC_FindHashedDefine(source->definehash, token.string); +#else + define = PC_FindDefine(source->defines, token.string); +#endif //DEFINEHASHING + } //end if + //allocate define + define = (define_t *) GetMemory(sizeof(define_t)); + Com_Memset(define, 0, sizeof(define_t)); + define->name = (char *) GetMemory(strlen(token.string) + 1); + strcpy(define->name, token.string); + //add the define to the source +#if DEFINEHASHING + PC_AddDefineToHash(define, source->definehash); +#else //DEFINEHASHING + define->next = source->defines; + source->defines = define; +#endif //DEFINEHASHING + //if nothing is defined, just return + if (!PC_ReadLine(source, &token)) return qtrue; + //if it is a define with parameters + if (!PC_WhiteSpaceBeforeToken(&token) && !strcmp(token.string, "(")) + { + //read the define parameters + last = NULL; + if (!PC_CheckTokenString(source, ")")) + { + while(1) + { + if (!PC_ReadLine(source, &token)) + { + SourceError(source, "expected define parameter"); + return qfalse; + } //end if + //if it isn't a name + if (token.type != TT_NAME) + { + SourceError(source, "invalid define parameter"); + return qfalse; + } //end if + // + if (PC_FindDefineParm(define, token.string) >= 0) + { + SourceError(source, "two the same define parameters"); + return qfalse; + } //end if + //add the define parm + t = PC_CopyToken(&token); + PC_ClearTokenWhiteSpace(t); + t->next = NULL; + if (last) last->next = t; + else define->parms = t; + last = t; + define->numparms++; + //read next token + if (!PC_ReadLine(source, &token)) + { + SourceError(source, "define parameters not terminated"); + return qfalse; + } //end if + // + if (!strcmp(token.string, ")")) break; + //then it must be a comma + if (strcmp(token.string, ",")) + { + SourceError(source, "define not terminated"); + return qfalse; + } //end if + } //end while + } //end if + if (!PC_ReadLine(source, &token)) return qtrue; + } //end if + //read the defined stuff + last = NULL; + do + { + t = PC_CopyToken(&token); + if (t->type == TT_NAME && !strcmp(t->string, define->name)) + { + SourceError(source, "recursive define (removed recursion)"); + continue; + } //end if + PC_ClearTokenWhiteSpace(t); + t->next = NULL; + if (last) last->next = t; + else define->tokens = t; + last = t; + } while(PC_ReadLine(source, &token)); + // + if (last) + { + //check for merge operators at the beginning or end + if (!strcmp(define->tokens->string, "##") || + !strcmp(last->string, "##")) + { + SourceError(source, "define with misplaced ##"); + return qfalse; + } //end if + } //end if + return qtrue; +} //end of the function PC_Directive_define +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +define_t *PC_DefineFromString(char *string) +{ + script_t *script; + source_t src; + token_t *t; + int res, i; + define_t *def; + + PC_InitTokenHeap(); + + script = LoadScriptMemory(string, strlen(string), "*extern"); + //create a new source + Com_Memset(&src, 0, sizeof(source_t)); + strncpy(src.filename, "*extern", MAX_PATH); + src.scriptstack = script; +#if DEFINEHASHING + src.definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *)); +#endif //DEFINEHASHING + //create a define from the source + res = PC_Directive_define(&src); + //free any tokens if left + for (t = src.tokens; t; t = src.tokens) + { + src.tokens = src.tokens->next; + PC_FreeToken(t); + } //end for +#ifdef DEFINEHASHING + def = NULL; + for (i = 0; i < DEFINEHASHSIZE; i++) + { + if (src.definehash[i]) + { + def = src.definehash[i]; + break; + } //end if + } //end for +#else + def = src.defines; +#endif //DEFINEHASHING + // +#if DEFINEHASHING + FreeMemory(src.definehash); +#endif //DEFINEHASHING + // + FreeScript(script); + //if the define was created succesfully + if (res > 0) return def; + //free the define is created + if (src.defines) PC_FreeDefine(def); + // + return NULL; +} //end of the function PC_DefineFromString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_AddDefine(source_t *source, char *string) +{ + define_t *define; + + define = PC_DefineFromString(string); + if (!define) return qfalse; +#if DEFINEHASHING + PC_AddDefineToHash(define, source->definehash); +#else //DEFINEHASHING + define->next = source->defines; + source->defines = define; +#endif //DEFINEHASHING + return qtrue; +} //end of the function PC_AddDefine +//============================================================================ +// add a globals define that will be added to all opened sources +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_AddGlobalDefine(char *string) +{ + define_t *define; + + define = PC_DefineFromString(string); + if (!define) return qfalse; + define->next = globaldefines; + globaldefines = define; + return qtrue; +} //end of the function PC_AddGlobalDefine +//============================================================================ +// remove the given global define +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_RemoveGlobalDefine(char *name) +{ + define_t *define; + + define = PC_FindDefine(globaldefines, name); + if (define) + { + PC_FreeDefine(define); + return qtrue; + } //end if + return qfalse; +} //end of the function PC_RemoveGlobalDefine +//============================================================================ +// remove all globals defines +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_RemoveAllGlobalDefines(void) +{ + define_t *define; + + for (define = globaldefines; define; define = globaldefines) + { + globaldefines = globaldefines->next; + PC_FreeDefine(define); + } //end for +} //end of the function PC_RemoveAllGlobalDefines +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +define_t *PC_CopyDefine(source_t *source, define_t *define) +{ + define_t *newdefine; + token_t *token, *newtoken, *lasttoken; + + newdefine = (define_t *) GetMemory(sizeof(define_t)); + //copy the define name + newdefine->name = (char *) GetMemory(strlen(define->name) + 1); + strcpy(newdefine->name, define->name); + newdefine->flags = define->flags; + newdefine->builtin = define->builtin; + newdefine->numparms = define->numparms; + //the define is not linked + newdefine->next = NULL; + newdefine->hashnext = NULL; + //copy the define tokens + newdefine->tokens = NULL; + for (lasttoken = NULL, token = define->tokens; token; token = token->next) + { + newtoken = PC_CopyToken(token); + newtoken->next = NULL; + if (lasttoken) lasttoken->next = newtoken; + else newdefine->tokens = newtoken; + lasttoken = newtoken; + } //end for + //copy the define parameters + newdefine->parms = NULL; + for (lasttoken = NULL, token = define->parms; token; token = token->next) + { + newtoken = PC_CopyToken(token); + newtoken->next = NULL; + if (lasttoken) lasttoken->next = newtoken; + else newdefine->parms = newtoken; + lasttoken = newtoken; + } //end for + return newdefine; +} //end of the function PC_CopyDefine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_AddGlobalDefinesToSource(source_t *source) +{ + define_t *define, *newdefine; + + for (define = globaldefines; define; define = define->next) + { + newdefine = PC_CopyDefine(source, define); +#if DEFINEHASHING + PC_AddDefineToHash(newdefine, source->definehash); +#else //DEFINEHASHING + newdefine->next = source->defines; + source->defines = newdefine; +#endif //DEFINEHASHING + } //end for +} //end of the function PC_AddGlobalDefinesToSource +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_if_def(source_t *source, int type) +{ + token_t token; + define_t *d; + int skip; + + if (!PC_ReadLine(source, &token)) + { + SourceError(source, "#ifdef without name"); + return qfalse; + } //end if + if (token.type != TT_NAME) + { + PC_UnreadSourceToken(source, &token); + SourceError(source, "expected name after #ifdef, found %s", token.string); + return qfalse; + } //end if +#if DEFINEHASHING + d = PC_FindHashedDefine(source->definehash, token.string); +#else + d = PC_FindDefine(source->defines, token.string); +#endif //DEFINEHASHING + skip = (type == INDENT_IFDEF) == (d == NULL); + PC_PushIndent(source, type, skip); + return qtrue; +} //end of the function PC_Directiveif_def +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_ifdef(source_t *source) +{ + return PC_Directive_if_def(source, INDENT_IFDEF); +} //end of the function PC_Directive_ifdef +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_ifndef(source_t *source) +{ + return PC_Directive_if_def(source, INDENT_IFNDEF); +} //end of the function PC_Directive_ifndef +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_else(source_t *source) +{ + int type, skip; + + PC_PopIndent(source, &type, &skip); + if (!type) + { + SourceError(source, "misplaced #else"); + return qfalse; + } //end if + if (type == INDENT_ELSE) + { + SourceError(source, "#else after #else"); + return qfalse; + } //end if + PC_PushIndent(source, INDENT_ELSE, !skip); + return qtrue; +} //end of the function PC_Directive_else +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_endif(source_t *source) +{ + int type, skip; + + PC_PopIndent(source, &type, &skip); + if (!type) + { + SourceError(source, "misplaced #endif"); + return qfalse; + } //end if + return qtrue; +} //end of the function PC_Directive_endif +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +typedef struct operator_s +{ + int operator; + int priority; + int parentheses; + struct operator_s *prev, *next; +} operator_t; + +typedef struct value_s +{ + signed long int intvalue; + float floatvalue; + int parentheses; + struct value_s *prev, *next; +} value_t; + +int PC_OperatorPriority(int op) +{ + switch(op) + { + case P_MUL: return 15; + case P_DIV: return 15; + case P_MOD: return 15; + case P_ADD: return 14; + case P_SUB: return 14; + + case P_LOGIC_AND: return 7; + case P_LOGIC_OR: return 6; + case P_LOGIC_GEQ: return 12; + case P_LOGIC_LEQ: return 12; + case P_LOGIC_EQ: return 11; + case P_LOGIC_UNEQ: return 11; + + case P_LOGIC_NOT: return 16; + case P_LOGIC_GREATER: return 12; + case P_LOGIC_LESS: return 12; + + case P_RSHIFT: return 13; + case P_LSHIFT: return 13; + + case P_BIN_AND: return 10; + case P_BIN_OR: return 8; + case P_BIN_XOR: return 9; + case P_BIN_NOT: return 16; + + case P_COLON: return 5; + case P_QUESTIONMARK: return 5; + } //end switch + return qfalse; +} //end of the function PC_OperatorPriority + +//#define AllocValue() GetClearedMemory(sizeof(value_t)); +//#define FreeValue(val) FreeMemory(val) +//#define AllocOperator(op) op = (operator_t *) GetClearedMemory(sizeof(operator_t)); +//#define FreeOperator(op) FreeMemory(op); + +#define MAX_VALUES 64 +#define MAX_OPERATORS 64 +#define AllocValue(val) \ + if (numvalues >= MAX_VALUES) { \ + SourceError(source, "out of value space\n"); \ + error = 1; \ + break; \ + } \ + else \ + val = &value_heap[numvalues++]; +#define FreeValue(val) +// +#define AllocOperator(op) \ + if (numoperators >= MAX_OPERATORS) { \ + SourceError(source, "out of operator space\n"); \ + error = 1; \ + break; \ + } \ + else \ + op = &operator_heap[numoperators++]; +#define FreeOperator(op) + +int PC_EvaluateTokens(source_t *source, token_t *tokens, signed long int *intvalue, + float *floatvalue, int integer) +{ + operator_t *o, *firstoperator, *lastoperator; + value_t *v, *firstvalue, *lastvalue, *v1, *v2; + token_t *t; + int brace = 0; + int parentheses = 0; + int error = 0; + int lastwasvalue = 0; + int negativevalue = 0; + int questmarkintvalue = 0; + float questmarkfloatvalue = 0; + int gotquestmarkvalue = qfalse; + int lastoperatortype = 0; + // + operator_t operator_heap[MAX_OPERATORS]; + int numoperators = 0; + value_t value_heap[MAX_VALUES]; + int numvalues = 0; + + firstoperator = lastoperator = NULL; + firstvalue = lastvalue = NULL; + if (intvalue) *intvalue = 0; + if (floatvalue) *floatvalue = 0; + for (t = tokens; t; t = t->next) + { + switch(t->type) + { + case TT_NAME: + { + if (lastwasvalue || negativevalue) + { + SourceError(source, "syntax error in #if/#elif"); + error = 1; + break; + } //end if + if (strcmp(t->string, "defined")) + { + SourceError(source, "undefined name %s in #if/#elif", t->string); + error = 1; + break; + } //end if + t = t->next; + if (!strcmp(t->string, "(")) + { + brace = qtrue; + t = t->next; + } //end if + if (!t || t->type != TT_NAME) + { + SourceError(source, "defined without name in #if/#elif"); + error = 1; + break; + } //end if + //v = (value_t *) GetClearedMemory(sizeof(value_t)); + AllocValue(v); +#if DEFINEHASHING + if (PC_FindHashedDefine(source->definehash, t->string)) +#else + if (PC_FindDefine(source->defines, t->string)) +#endif //DEFINEHASHING + { + v->intvalue = 1; + v->floatvalue = 1; + } //end if + else + { + v->intvalue = 0; + v->floatvalue = 0; + } //end else + v->parentheses = parentheses; + v->next = NULL; + v->prev = lastvalue; + if (lastvalue) lastvalue->next = v; + else firstvalue = v; + lastvalue = v; + if (brace) + { + t = t->next; + if (!t || strcmp(t->string, ")")) + { + SourceError(source, "defined without ) in #if/#elif"); + error = 1; + break; + } //end if + } //end if + brace = qfalse; + // defined() creates a value + lastwasvalue = 1; + break; + } //end case + case TT_NUMBER: + { + if (lastwasvalue) + { + SourceError(source, "syntax error in #if/#elif"); + error = 1; + break; + } //end if + //v = (value_t *) GetClearedMemory(sizeof(value_t)); + AllocValue(v); + if (negativevalue) + { + v->intvalue = - (signed int) t->intvalue; + v->floatvalue = - t->floatvalue; + } //end if + else + { + v->intvalue = t->intvalue; + v->floatvalue = t->floatvalue; + } //end else + v->parentheses = parentheses; + v->next = NULL; + v->prev = lastvalue; + if (lastvalue) lastvalue->next = v; + else firstvalue = v; + lastvalue = v; + //last token was a value + lastwasvalue = 1; + // + negativevalue = 0; + break; + } //end case + case TT_PUNCTUATION: + { + if (negativevalue) + { + SourceError(source, "misplaced minus sign in #if/#elif"); + error = 1; + break; + } //end if + if (t->subtype == P_PARENTHESESOPEN) + { + parentheses++; + break; + } //end if + else if (t->subtype == P_PARENTHESESCLOSE) + { + parentheses--; + if (parentheses < 0) + { + SourceError(source, "too many ) in #if/#elsif"); + error = 1; + } //end if + break; + } //end else if + //check for invalid operators on floating point values + if (!integer) + { + if (t->subtype == P_BIN_NOT || t->subtype == P_MOD || + t->subtype == P_RSHIFT || t->subtype == P_LSHIFT || + t->subtype == P_BIN_AND || t->subtype == P_BIN_OR || + t->subtype == P_BIN_XOR) + { + SourceError(source, "illigal operator %s on floating point operands\n", t->string); + error = 1; + break; + } //end if + } //end if + switch(t->subtype) + { + case P_LOGIC_NOT: + case P_BIN_NOT: + { + if (lastwasvalue) + { + SourceError(source, "! or ~ after value in #if/#elif"); + error = 1; + break; + } //end if + break; + } //end case + case P_INC: + case P_DEC: + { + SourceError(source, "++ or -- used in #if/#elif"); + break; + } //end case + case P_SUB: + { + if (!lastwasvalue) + { + negativevalue = 1; + break; + } //end if + } //end case + + case P_MUL: + case P_DIV: + case P_MOD: + case P_ADD: + + case P_LOGIC_AND: + case P_LOGIC_OR: + case P_LOGIC_GEQ: + case P_LOGIC_LEQ: + case P_LOGIC_EQ: + case P_LOGIC_UNEQ: + + case P_LOGIC_GREATER: + case P_LOGIC_LESS: + + case P_RSHIFT: + case P_LSHIFT: + + case P_BIN_AND: + case P_BIN_OR: + case P_BIN_XOR: + + case P_COLON: + case P_QUESTIONMARK: + { + if (!lastwasvalue) + { + SourceError(source, "operator %s after operator in #if/#elif", t->string); + error = 1; + break; + } //end if + break; + } //end case + default: + { + SourceError(source, "invalid operator %s in #if/#elif", t->string); + error = 1; + break; + } //end default + } //end switch + if (!error && !negativevalue) + { + //o = (operator_t *) GetClearedMemory(sizeof(operator_t)); + AllocOperator(o); + o->operator = t->subtype; + o->priority = PC_OperatorPriority(t->subtype); + o->parentheses = parentheses; + o->next = NULL; + o->prev = lastoperator; + if (lastoperator) lastoperator->next = o; + else firstoperator = o; + lastoperator = o; + lastwasvalue = 0; + } //end if + break; + } //end case + default: + { + SourceError(source, "unknown %s in #if/#elif", t->string); + error = 1; + break; + } //end default + } //end switch + if (error) break; + } //end for + if (!error) + { + if (!lastwasvalue) + { + SourceError(source, "trailing operator in #if/#elif"); + error = 1; + } //end if + else if (parentheses) + { + SourceError(source, "too many ( in #if/#elif"); + error = 1; + } //end else if + } //end if + // + gotquestmarkvalue = qfalse; + questmarkintvalue = 0; + questmarkfloatvalue = 0; + //while there are operators + while(!error && firstoperator) + { + v = firstvalue; + for (o = firstoperator; o->next; o = o->next) + { + //if the current operator is nested deeper in parentheses + //than the next operator + if (o->parentheses > o->next->parentheses) break; + //if the current and next operator are nested equally deep in parentheses + if (o->parentheses == o->next->parentheses) + { + //if the priority of the current operator is equal or higher + //than the priority of the next operator + if (o->priority >= o->next->priority) break; + } //end if + //if the arity of the operator isn't equal to 1 + if (o->operator != P_LOGIC_NOT + && o->operator != P_BIN_NOT) v = v->next; + //if there's no value or no next value + if (!v) + { + SourceError(source, "mising values in #if/#elif"); + error = 1; + break; + } //end if + } //end for + if (error) break; + v1 = v; + v2 = v->next; +#ifdef DEBUG_EVAL + if (integer) + { + Log_Write("operator %s, value1 = %d", PunctuationFromNum(source->scriptstack, o->operator), v1->intvalue); + if (v2) Log_Write("value2 = %d", v2->intvalue); + } //end if + else + { + Log_Write("operator %s, value1 = %f", PunctuationFromNum(source->scriptstack, o->operator), v1->floatvalue); + if (v2) Log_Write("value2 = %f", v2->floatvalue); + } //end else +#endif //DEBUG_EVAL + switch(o->operator) + { + case P_LOGIC_NOT: v1->intvalue = !v1->intvalue; + v1->floatvalue = !v1->floatvalue; break; + case P_BIN_NOT: v1->intvalue = ~v1->intvalue; + break; + case P_MUL: v1->intvalue *= v2->intvalue; + v1->floatvalue *= v2->floatvalue; break; + case P_DIV: if (!v2->intvalue || !v2->floatvalue) + { + SourceError(source, "divide by zero in #if/#elif\n"); + error = 1; + break; + } + v1->intvalue /= v2->intvalue; + v1->floatvalue /= v2->floatvalue; break; + case P_MOD: if (!v2->intvalue) + { + SourceError(source, "divide by zero in #if/#elif\n"); + error = 1; + break; + } + v1->intvalue %= v2->intvalue; break; + case P_ADD: v1->intvalue += v2->intvalue; + v1->floatvalue += v2->floatvalue; break; + case P_SUB: v1->intvalue -= v2->intvalue; + v1->floatvalue -= v2->floatvalue; break; + case P_LOGIC_AND: v1->intvalue = v1->intvalue && v2->intvalue; + v1->floatvalue = v1->floatvalue && v2->floatvalue; break; + case P_LOGIC_OR: v1->intvalue = v1->intvalue || v2->intvalue; + v1->floatvalue = v1->floatvalue || v2->floatvalue; break; + case P_LOGIC_GEQ: v1->intvalue = v1->intvalue >= v2->intvalue; + v1->floatvalue = v1->floatvalue >= v2->floatvalue; break; + case P_LOGIC_LEQ: v1->intvalue = v1->intvalue <= v2->intvalue; + v1->floatvalue = v1->floatvalue <= v2->floatvalue; break; + case P_LOGIC_EQ: v1->intvalue = v1->intvalue == v2->intvalue; + v1->floatvalue = v1->floatvalue == v2->floatvalue; break; + case P_LOGIC_UNEQ: v1->intvalue = v1->intvalue != v2->intvalue; + v1->floatvalue = v1->floatvalue != v2->floatvalue; break; + case P_LOGIC_GREATER: v1->intvalue = v1->intvalue > v2->intvalue; + v1->floatvalue = v1->floatvalue > v2->floatvalue; break; + case P_LOGIC_LESS: v1->intvalue = v1->intvalue < v2->intvalue; + v1->floatvalue = v1->floatvalue < v2->floatvalue; break; + case P_RSHIFT: v1->intvalue >>= v2->intvalue; + break; + case P_LSHIFT: v1->intvalue <<= v2->intvalue; + break; + case P_BIN_AND: v1->intvalue &= v2->intvalue; + break; + case P_BIN_OR: v1->intvalue |= v2->intvalue; + break; + case P_BIN_XOR: v1->intvalue ^= v2->intvalue; + break; + case P_COLON: + { + if (!gotquestmarkvalue) + { + SourceError(source, ": without ? in #if/#elif"); + error = 1; + break; + } //end if + if (integer) + { + if (!questmarkintvalue) v1->intvalue = v2->intvalue; + } //end if + else + { + if (!questmarkfloatvalue) v1->floatvalue = v2->floatvalue; + } //end else + gotquestmarkvalue = qfalse; + break; + } //end case + case P_QUESTIONMARK: + { + if (gotquestmarkvalue) + { + SourceError(source, "? after ? in #if/#elif"); + error = 1; + break; + } //end if + questmarkintvalue = v1->intvalue; + questmarkfloatvalue = v1->floatvalue; + gotquestmarkvalue = qtrue; + break; + } //end if + } //end switch +#ifdef DEBUG_EVAL + if (integer) Log_Write("result value = %d", v1->intvalue); + else Log_Write("result value = %f", v1->floatvalue); +#endif //DEBUG_EVAL + if (error) break; + lastoperatortype = o->operator; + //if not an operator with arity 1 + if (o->operator != P_LOGIC_NOT + && o->operator != P_BIN_NOT) + { + //remove the second value if not question mark operator + if (o->operator != P_QUESTIONMARK) v = v->next; + // + if (v->prev) v->prev->next = v->next; + else firstvalue = v->next; + if (v->next) v->next->prev = v->prev; + else lastvalue = v->prev; + //FreeMemory(v); + FreeValue(v); + } //end if + //remove the operator + if (o->prev) o->prev->next = o->next; + else firstoperator = o->next; + if (o->next) o->next->prev = o->prev; + else lastoperator = o->prev; + //FreeMemory(o); + FreeOperator(o); + } //end while + if (firstvalue) + { + if (intvalue) *intvalue = firstvalue->intvalue; + if (floatvalue) *floatvalue = firstvalue->floatvalue; + } //end if + for (o = firstoperator; o; o = lastoperator) + { + lastoperator = o->next; + //FreeMemory(o); + FreeOperator(o); + } //end for + for (v = firstvalue; v; v = lastvalue) + { + lastvalue = v->next; + //FreeMemory(v); + FreeValue(v); + } //end for + if (!error) return qtrue; + if (intvalue) *intvalue = 0; + if (floatvalue) *floatvalue = 0; + return qfalse; +} //end of the function PC_EvaluateTokens +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Evaluate(source_t *source, signed long int *intvalue, + float *floatvalue, int integer) +{ + token_t token, *firsttoken, *lasttoken; + token_t *t, *nexttoken; + define_t *define; + int defined = qfalse; + + if (intvalue) *intvalue = 0; + if (floatvalue) *floatvalue = 0; + // + if (!PC_ReadLine(source, &token)) + { + SourceError(source, "no value after #if/#elif"); + return qfalse; + } //end if + firsttoken = NULL; + lasttoken = NULL; + do + { + //if the token is a name + if (token.type == TT_NAME) + { + if (defined) + { + defined = qfalse; + t = PC_CopyToken(&token); + t->next = NULL; + if (lasttoken) lasttoken->next = t; + else firsttoken = t; + lasttoken = t; + } //end if + else if (!strcmp(token.string, "defined")) + { + defined = qtrue; + t = PC_CopyToken(&token); + t->next = NULL; + if (lasttoken) lasttoken->next = t; + else firsttoken = t; + lasttoken = t; + } //end if + else + { + //then it must be a define +#if DEFINEHASHING + define = PC_FindHashedDefine(source->definehash, token.string); +#else + define = PC_FindDefine(source->defines, token.string); +#endif //DEFINEHASHING + if (!define) + { + SourceError(source, "can't evaluate %s, not defined", token.string); + return qfalse; + } //end if + if (!PC_ExpandDefineIntoSource(source, &token, define)) return qfalse; + } //end else + } //end if + //if the token is a number or a punctuation + else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION) + { + t = PC_CopyToken(&token); + t->next = NULL; + if (lasttoken) lasttoken->next = t; + else firsttoken = t; + lasttoken = t; + } //end else + else //can't evaluate the token + { + SourceError(source, "can't evaluate %s", token.string); + return qfalse; + } //end else + } while(PC_ReadLine(source, &token)); + // + if (!PC_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse; + // +#ifdef DEBUG_EVAL + Log_Write("eval:"); +#endif //DEBUG_EVAL + for (t = firsttoken; t; t = nexttoken) + { +#ifdef DEBUG_EVAL + Log_Write(" %s", t->string); +#endif //DEBUG_EVAL + nexttoken = t->next; + PC_FreeToken(t); + } //end for +#ifdef DEBUG_EVAL + if (integer) Log_Write("eval result: %d", *intvalue); + else Log_Write("eval result: %f", *floatvalue); +#endif //DEBUG_EVAL + // + return qtrue; +} //end of the function PC_Evaluate +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_DollarEvaluate(source_t *source, signed long int *intvalue, + float *floatvalue, int integer) +{ + int indent, defined = qfalse; + token_t token, *firsttoken, *lasttoken; + token_t *t, *nexttoken; + define_t *define; + + if (intvalue) *intvalue = 0; + if (floatvalue) *floatvalue = 0; + // + if (!PC_ReadSourceToken(source, &token)) + { + SourceError(source, "no leading ( after $evalint/$evalfloat"); + return qfalse; + } //end if + if (!PC_ReadSourceToken(source, &token)) + { + SourceError(source, "nothing to evaluate"); + return qfalse; + } //end if + indent = 1; + firsttoken = NULL; + lasttoken = NULL; + do + { + //if the token is a name + if (token.type == TT_NAME) + { + if (defined) + { + defined = qfalse; + t = PC_CopyToken(&token); + t->next = NULL; + if (lasttoken) lasttoken->next = t; + else firsttoken = t; + lasttoken = t; + } //end if + else if (!strcmp(token.string, "defined")) + { + defined = qtrue; + t = PC_CopyToken(&token); + t->next = NULL; + if (lasttoken) lasttoken->next = t; + else firsttoken = t; + lasttoken = t; + } //end if + else + { + //then it must be a define +#if DEFINEHASHING + define = PC_FindHashedDefine(source->definehash, token.string); +#else + define = PC_FindDefine(source->defines, token.string); +#endif //DEFINEHASHING + if (!define) + { + SourceError(source, "can't evaluate %s, not defined", token.string); + return qfalse; + } //end if + if (!PC_ExpandDefineIntoSource(source, &token, define)) return qfalse; + } //end else + } //end if + //if the token is a number or a punctuation + else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION) + { + if (*token.string == '(') indent++; + else if (*token.string == ')') indent--; + if (indent <= 0) break; + t = PC_CopyToken(&token); + t->next = NULL; + if (lasttoken) lasttoken->next = t; + else firsttoken = t; + lasttoken = t; + } //end else + else //can't evaluate the token + { + SourceError(source, "can't evaluate %s", token.string); + return qfalse; + } //end else + } while(PC_ReadSourceToken(source, &token)); + // + if (!PC_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse; + // +#ifdef DEBUG_EVAL + Log_Write("$eval:"); +#endif //DEBUG_EVAL + for (t = firsttoken; t; t = nexttoken) + { +#ifdef DEBUG_EVAL + Log_Write(" %s", t->string); +#endif //DEBUG_EVAL + nexttoken = t->next; + PC_FreeToken(t); + } //end for +#ifdef DEBUG_EVAL + if (integer) Log_Write("$eval result: %d", *intvalue); + else Log_Write("$eval result: %f", *floatvalue); +#endif //DEBUG_EVAL + // + return qtrue; +} //end of the function PC_DollarEvaluate +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_elif(source_t *source) +{ + signed long int value; + int type, skip; + + PC_PopIndent(source, &type, &skip); + if (!type || type == INDENT_ELSE) + { + SourceError(source, "misplaced #elif"); + return qfalse; + } //end if + if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse; + skip = (value == 0); + PC_PushIndent(source, INDENT_ELIF, skip); + return qtrue; +} //end of the function PC_Directive_elif +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_if(source_t *source) +{ + signed long int value; + int skip; + + if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse; + skip = (value == 0); + PC_PushIndent(source, INDENT_IF, skip); + return qtrue; +} //end of the function PC_Directive +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_line(source_t *source) +{ + SourceError(source, "#line directive not supported"); + return qfalse; +} //end of the function PC_Directive_line +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_error(source_t *source) +{ + token_t token; + + strcpy(token.string, ""); + PC_ReadSourceToken(source, &token); + SourceError(source, "#error directive: %s", token.string); + return qfalse; +} //end of the function PC_Directive_error +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_pragma(source_t *source) +{ + token_t token; + + SourceWarning(source, "#pragma directive not supported"); + while(PC_ReadLine(source, &token)) ; + return qtrue; +} //end of the function PC_Directive_pragma +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void UnreadSignToken(source_t *source) +{ + token_t token; + + token.line = source->scriptstack->line; + token.whitespace_p = source->scriptstack->script_p; + token.endwhitespace_p = source->scriptstack->script_p; + token.linescrossed = 0; + strcpy(token.string, "-"); + token.type = TT_PUNCTUATION; + token.subtype = P_SUB; + PC_UnreadSourceToken(source, &token); +} //end of the function UnreadSignToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_eval(source_t *source) +{ + signed long int value; + token_t token; + + if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse; + // + token.line = source->scriptstack->line; + token.whitespace_p = source->scriptstack->script_p; + token.endwhitespace_p = source->scriptstack->script_p; + token.linescrossed = 0; + sprintf(token.string, "%d", abs(value)); + token.type = TT_NUMBER; + token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL; + PC_UnreadSourceToken(source, &token); + if (value < 0) UnreadSignToken(source); + return qtrue; +} //end of the function PC_Directive_eval +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_evalfloat(source_t *source) +{ + float value; + token_t token; + + if (!PC_Evaluate(source, NULL, &value, qfalse)) return qfalse; + token.line = source->scriptstack->line; + token.whitespace_p = source->scriptstack->script_p; + token.endwhitespace_p = source->scriptstack->script_p; + token.linescrossed = 0; + sprintf(token.string, "%1.2f", fabs(value)); + token.type = TT_NUMBER; + token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL; + PC_UnreadSourceToken(source, &token); + if (value < 0) UnreadSignToken(source); + return qtrue; +} //end of the function PC_Directive_evalfloat +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +directive_t directives[20] = +{ + {"if", PC_Directive_if}, + {"ifdef", PC_Directive_ifdef}, + {"ifndef", PC_Directive_ifndef}, + {"elif", PC_Directive_elif}, + {"else", PC_Directive_else}, + {"endif", PC_Directive_endif}, + {"include", PC_Directive_include}, + {"define", PC_Directive_define}, + {"undef", PC_Directive_undef}, + {"line", PC_Directive_line}, + {"error", PC_Directive_error}, + {"pragma", PC_Directive_pragma}, + {"eval", PC_Directive_eval}, + {"evalfloat", PC_Directive_evalfloat}, + {NULL, NULL} +}; + +int PC_ReadDirective(source_t *source) +{ + token_t token; + int i; + + //read the directive name + if (!PC_ReadSourceToken(source, &token)) + { + SourceError(source, "found # without name"); + return qfalse; + } //end if + //directive name must be on the same line + if (token.linescrossed > 0) + { + PC_UnreadSourceToken(source, &token); + SourceError(source, "found # at end of line"); + return qfalse; + } //end if + //if if is a name + if (token.type == TT_NAME) + { + //find the precompiler directive + for (i = 0; directives[i].name; i++) + { + if (!strcmp(directives[i].name, token.string)) + { + return directives[i].func(source); + } //end if + } //end for + } //end if + SourceError(source, "unknown precompiler directive %s", token.string); + return qfalse; +} //end of the function PC_ReadDirective +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_DollarDirective_evalint(source_t *source) +{ + signed long int value; + token_t token; + + if (!PC_DollarEvaluate(source, &value, NULL, qtrue)) return qfalse; + // + token.line = source->scriptstack->line; + token.whitespace_p = source->scriptstack->script_p; + token.endwhitespace_p = source->scriptstack->script_p; + token.linescrossed = 0; + sprintf(token.string, "%d", abs(value)); + token.type = TT_NUMBER; + token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL; +#ifdef NUMBERVALUE + token.intvalue = value; + token.floatvalue = value; +#endif //NUMBERVALUE + PC_UnreadSourceToken(source, &token); + if (value < 0) UnreadSignToken(source); + return qtrue; +} //end of the function PC_DollarDirective_evalint +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_DollarDirective_evalfloat(source_t *source) +{ + float value; + token_t token; + + if (!PC_DollarEvaluate(source, NULL, &value, qfalse)) return qfalse; + token.line = source->scriptstack->line; + token.whitespace_p = source->scriptstack->script_p; + token.endwhitespace_p = source->scriptstack->script_p; + token.linescrossed = 0; + sprintf(token.string, "%1.2f", fabs(value)); + token.type = TT_NUMBER; + token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL; +#ifdef NUMBERVALUE + token.intvalue = (unsigned long) value; + token.floatvalue = value; +#endif //NUMBERVALUE + PC_UnreadSourceToken(source, &token); + if (value < 0) UnreadSignToken(source); + return qtrue; +} //end of the function PC_DollarDirective_evalfloat +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +directive_t dollardirectives[20] = +{ + {"evalint", PC_DollarDirective_evalint}, + {"evalfloat", PC_DollarDirective_evalfloat}, + {NULL, NULL} +}; + +int PC_ReadDollarDirective(source_t *source) +{ + token_t token; + int i; + + //read the directive name + if (!PC_ReadSourceToken(source, &token)) + { + SourceError(source, "found $ without name"); + return qfalse; + } //end if + //directive name must be on the same line + if (token.linescrossed > 0) + { + PC_UnreadSourceToken(source, &token); + SourceError(source, "found $ at end of line"); + return qfalse; + } //end if + //if if is a name + if (token.type == TT_NAME) + { + //find the precompiler directive + for (i = 0; dollardirectives[i].name; i++) + { + if (!strcmp(dollardirectives[i].name, token.string)) + { + return dollardirectives[i].func(source); + } //end if + } //end for + } //end if + PC_UnreadSourceToken(source, &token); + SourceError(source, "unknown precompiler directive %s", token.string); + return qfalse; +} //end of the function PC_ReadDirective + +#ifdef QUAKEC +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int BuiltinFunction(source_t *source) +{ + token_t token; + + if (!PC_ReadSourceToken(source, &token)) return qfalse; + if (token.type == TT_NUMBER) + { + PC_UnreadSourceToken(source, &token); + return qtrue; + } //end if + else + { + PC_UnreadSourceToken(source, &token); + return qfalse; + } //end else +} //end of the function BuiltinFunction +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int QuakeCMacro(source_t *source) +{ + int i; + token_t token; + + if (!PC_ReadSourceToken(source, &token)) return qtrue; + if (token.type != TT_NAME) + { + PC_UnreadSourceToken(source, &token); + return qtrue; + } //end if + //find the precompiler directive + for (i = 0; dollardirectives[i].name; i++) + { + if (!strcmp(dollardirectives[i].name, token.string)) + { + PC_UnreadSourceToken(source, &token); + return qfalse; + } //end if + } //end for + PC_UnreadSourceToken(source, &token); + return qtrue; +} //end of the function QuakeCMacro +#endif //QUAKEC +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ReadToken(source_t *source, token_t *token) +{ + define_t *define; + + while(1) + { + if (!PC_ReadSourceToken(source, token)) return qfalse; + //check for precompiler directives + if (token->type == TT_PUNCTUATION && *token->string == '#') + { +#ifdef QUAKEC + if (!BuiltinFunction(source)) +#endif //QUAKC + { + //read the precompiler directive + if (!PC_ReadDirective(source)) return qfalse; + continue; + } //end if + } //end if + if (token->type == TT_PUNCTUATION && *token->string == '$') + { +#ifdef QUAKEC + if (!QuakeCMacro(source)) +#endif //QUAKEC + { + //read the precompiler directive + if (!PC_ReadDollarDirective(source)) return qfalse; + continue; + } //end if + } //end if + // recursively concatenate strings that are behind each other still resolving defines + if (token->type == TT_STRING) + { + token_t newtoken; + if (PC_ReadToken(source, &newtoken)) + { + if (newtoken.type == TT_STRING) + { + token->string[strlen(token->string)-1] = '\0'; + if (strlen(token->string) + strlen(newtoken.string+1) + 1 >= MAX_TOKEN) + { + SourceError(source, "string longer than MAX_TOKEN %d\n", MAX_TOKEN); + return qfalse; + } + strcat(token->string, newtoken.string+1); + } + else + { + PC_UnreadToken(source, &newtoken); + } + } + } //end if + //if skipping source because of conditional compilation + if (source->skip) continue; + //if the token is a name + if (token->type == TT_NAME) + { + //check if the name is a define macro +#if DEFINEHASHING + define = PC_FindHashedDefine(source->definehash, token->string); +#else + define = PC_FindDefine(source->defines, token->string); +#endif //DEFINEHASHING + //if it is a define macro + if (define) + { + //expand the defined macro + if (!PC_ExpandDefineIntoSource(source, token, define)) return qfalse; + continue; + } //end if + } //end if + //copy token for unreading + Com_Memcpy(&source->token, token, sizeof(token_t)); + //found a token + return qtrue; + } //end while +} //end of the function PC_ReadToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpectTokenString(source_t *source, char *string) +{ + token_t token; + + if (!PC_ReadToken(source, &token)) + { + SourceError(source, "couldn't find expected %s", string); + return qfalse; + } //end if + + if (strcmp(token.string, string)) + { + SourceError(source, "expected %s, found %s", string, token.string); + return qfalse; + } //end if + return qtrue; +} //end of the function PC_ExpectTokenString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpectTokenType(source_t *source, int type, int subtype, token_t *token) +{ + char str[MAX_TOKEN]; + + if (!PC_ReadToken(source, token)) + { + SourceError(source, "couldn't read expected token"); + return qfalse; + } //end if + + if (token->type != type) + { + strcpy(str, ""); + if (type == TT_STRING) strcpy(str, "string"); + if (type == TT_LITERAL) strcpy(str, "literal"); + if (type == TT_NUMBER) strcpy(str, "number"); + if (type == TT_NAME) strcpy(str, "name"); + if (type == TT_PUNCTUATION) strcpy(str, "punctuation"); + SourceError(source, "expected a %s, found %s", str, token->string); + return qfalse; + } //end if + if (token->type == TT_NUMBER) + { + if ((token->subtype & subtype) != subtype) + { + if (subtype & TT_DECIMAL) strcpy(str, "decimal"); + if (subtype & TT_HEX) strcpy(str, "hex"); + if (subtype & TT_OCTAL) strcpy(str, "octal"); + if (subtype & TT_BINARY) strcpy(str, "binary"); + if (subtype & TT_LONG) strcat(str, " long"); + if (subtype & TT_UNSIGNED) strcat(str, " unsigned"); + if (subtype & TT_FLOAT) strcat(str, " float"); + if (subtype & TT_INTEGER) strcat(str, " integer"); + SourceError(source, "expected %s, found %s", str, token->string); + return qfalse; + } //end if + } //end if + else if (token->type == TT_PUNCTUATION) + { + if (token->subtype != subtype) + { + SourceError(source, "found %s", token->string); + return qfalse; + } //end if + } //end else if + return qtrue; +} //end of the function PC_ExpectTokenType +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpectAnyToken(source_t *source, token_t *token) +{ + if (!PC_ReadToken(source, token)) + { + SourceError(source, "couldn't read expected token"); + return qfalse; + } //end if + else + { + return qtrue; + } //end else +} //end of the function PC_ExpectAnyToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_CheckTokenString(source_t *source, char *string) +{ + token_t tok; + + if (!PC_ReadToken(source, &tok)) return qfalse; + //if the token is available + if (!strcmp(tok.string, string)) return qtrue; + // + PC_UnreadSourceToken(source, &tok); + return qfalse; +} //end of the function PC_CheckTokenString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_CheckTokenType(source_t *source, int type, int subtype, token_t *token) +{ + token_t tok; + + if (!PC_ReadToken(source, &tok)) return qfalse; + //if the type matches + if (tok.type == type && + (tok.subtype & subtype) == subtype) + { + Com_Memcpy(token, &tok, sizeof(token_t)); + return qtrue; + } //end if + // + PC_UnreadSourceToken(source, &tok); + return qfalse; +} //end of the function PC_CheckTokenType +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_SkipUntilString(source_t *source, char *string) +{ + token_t token; + + while(PC_ReadToken(source, &token)) + { + if (!strcmp(token.string, string)) return qtrue; + } //end while + return qfalse; +} //end of the function PC_SkipUntilString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_UnreadLastToken(source_t *source) +{ + PC_UnreadSourceToken(source, &source->token); +} //end of the function PC_UnreadLastToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_UnreadToken(source_t *source, token_t *token) +{ + PC_UnreadSourceToken(source, token); +} //end of the function PC_UnreadToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_SetIncludePath(source_t *source, char *path) +{ + strncpy(source->includepath, path, MAX_PATH); + //add trailing path seperator + if (source->includepath[strlen(source->includepath)-1] != '\\' && + source->includepath[strlen(source->includepath)-1] != '/') + { + strcat(source->includepath, PATHSEPERATOR_STR); + } //end if +} //end of the function PC_SetIncludePath +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_SetPunctuations(source_t *source, punctuation_t *p) +{ + source->punctuations = p; +} //end of the function PC_SetPunctuations +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +source_t *LoadSourceFile(const char *filename) +{ + source_t *source; + script_t *script; + + PC_InitTokenHeap(); + + script = LoadScriptFile(filename); + if (!script) return NULL; + + script->next = NULL; + + source = (source_t *) GetMemory(sizeof(source_t)); + Com_Memset(source, 0, sizeof(source_t)); + + strncpy(source->filename, filename, MAX_PATH); + source->scriptstack = script; + source->tokens = NULL; + source->defines = NULL; + source->indentstack = NULL; + source->skip = 0; + +#if DEFINEHASHING + source->definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *)); +#endif //DEFINEHASHING + PC_AddGlobalDefinesToSource(source); + return source; +} //end of the function LoadSourceFile +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +source_t *LoadSourceMemory(char *ptr, int length, char *name) +{ + source_t *source; + script_t *script; + + PC_InitTokenHeap(); + + script = LoadScriptMemory(ptr, length, name); + if (!script) return NULL; + script->next = NULL; + + source = (source_t *) GetMemory(sizeof(source_t)); + Com_Memset(source, 0, sizeof(source_t)); + + strncpy(source->filename, name, MAX_PATH); + source->scriptstack = script; + source->tokens = NULL; + source->defines = NULL; + source->indentstack = NULL; + source->skip = 0; + +#if DEFINEHASHING + source->definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *)); +#endif //DEFINEHASHING + PC_AddGlobalDefinesToSource(source); + return source; +} //end of the function LoadSourceMemory +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void FreeSource(source_t *source) +{ + script_t *script; + token_t *token; + define_t *define; + indent_t *indent; + int i; + + //PC_PrintDefineHashTable(source->definehash); + //free all the scripts + while(source->scriptstack) + { + script = source->scriptstack; + source->scriptstack = source->scriptstack->next; + FreeScript(script); + } //end for + //free all the tokens + while(source->tokens) + { + token = source->tokens; + source->tokens = source->tokens->next; + PC_FreeToken(token); + } //end for +#if DEFINEHASHING + for (i = 0; i < DEFINEHASHSIZE; i++) + { + while(source->definehash[i]) + { + define = source->definehash[i]; + source->definehash[i] = source->definehash[i]->hashnext; + PC_FreeDefine(define); + } //end while + } //end for +#else //DEFINEHASHING + //free all defines + while(source->defines) + { + define = source->defines; + source->defines = source->defines->next; + PC_FreeDefine(define); + } //end for +#endif //DEFINEHASHING + //free all indents + while(source->indentstack) + { + indent = source->indentstack; + source->indentstack = source->indentstack->next; + FreeMemory(indent); + } //end for +#if DEFINEHASHING + // + if (source->definehash) FreeMemory(source->definehash); +#endif //DEFINEHASHING + //free the source itself + FreeMemory(source); +} //end of the function FreeSource +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ + +#define MAX_SOURCEFILES 64 + +source_t *sourceFiles[MAX_SOURCEFILES]; + +int PC_LoadSourceHandle(const char *filename) +{ + source_t *source; + int i; + + for (i = 1; i < MAX_SOURCEFILES; i++) + { + if (!sourceFiles[i]) + break; + } //end for + if (i >= MAX_SOURCEFILES) + return 0; + PS_SetBaseFolder(""); + source = LoadSourceFile(filename); + if (!source) + return 0; + sourceFiles[i] = source; + return i; +} //end of the function PC_LoadSourceHandle +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_FreeSourceHandle(int handle) +{ + if (handle < 1 || handle >= MAX_SOURCEFILES) + return qfalse; + if (!sourceFiles[handle]) + return qfalse; + + FreeSource(sourceFiles[handle]); + sourceFiles[handle] = NULL; + return qtrue; +} //end of the function PC_FreeSourceHandle +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ReadTokenHandle(int handle, pc_token_t *pc_token) +{ + token_t token; + int ret; + + if (handle < 1 || handle >= MAX_SOURCEFILES) + return 0; + if (!sourceFiles[handle]) + return 0; + + ret = PC_ReadToken(sourceFiles[handle], &token); + strcpy(pc_token->string, token.string); + pc_token->type = token.type; + pc_token->subtype = token.subtype; + pc_token->intvalue = token.intvalue; + pc_token->floatvalue = token.floatvalue; + if (pc_token->type == TT_STRING) + StripDoubleQuotes(pc_token->string); + return ret; +} //end of the function PC_ReadTokenHandle +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_SourceFileAndLine(int handle, char *filename, int *line) +{ + if (handle < 1 || handle >= MAX_SOURCEFILES) + return qfalse; + if (!sourceFiles[handle]) + return qfalse; + + strcpy(filename, sourceFiles[handle]->filename); + if (sourceFiles[handle]->scriptstack) + *line = sourceFiles[handle]->scriptstack->line; + else + *line = 0; + return qtrue; +} //end of the function PC_SourceFileAndLine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_SetBaseFolder(char *path) +{ + PS_SetBaseFolder(path); +} //end of the function PC_SetBaseFolder +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_CheckOpenSourceHandles(void) +{ + int i; + + for (i = 1; i < MAX_SOURCEFILES; i++) + { + if (sourceFiles[i]) + { +#ifdef BOTLIB + botimport.Print(PRT_ERROR, "file %s still open in precompiler\n", sourceFiles[i]->scriptstack->filename); +#endif //BOTLIB + } //end if + } //end for +} //end of the function PC_CheckOpenSourceHandles + diff --git a/reaction/engine/code/botlib/l_precomp.h b/reaction/engine/code/botlib/l_precomp.h new file mode 100644 index 00000000..fcc0e8a3 --- /dev/null +++ b/reaction/engine/code/botlib/l_precomp.h @@ -0,0 +1,180 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_precomp.h + * + * desc: pre compiler + * + * $Archive: /source/code/botlib/l_precomp.h $ + * + *****************************************************************************/ + +#ifndef MAX_PATH + #define MAX_PATH MAX_QPATH +#endif + +#ifndef PATH_SEPERATORSTR + #if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__) + #define PATHSEPERATOR_STR "\\" + #else + #define PATHSEPERATOR_STR "/" + #endif +#endif +#ifndef PATH_SEPERATORCHAR + #if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__) + #define PATHSEPERATOR_CHAR '\\' + #else + #define PATHSEPERATOR_CHAR '/' + #endif +#endif + +#if defined(BSPC) && !defined(QDECL) +#define QDECL +#endif + + +#define DEFINE_FIXED 0x0001 + +#define BUILTIN_LINE 1 +#define BUILTIN_FILE 2 +#define BUILTIN_DATE 3 +#define BUILTIN_TIME 4 +#define BUILTIN_STDC 5 + +#define INDENT_IF 0x0001 +#define INDENT_ELSE 0x0002 +#define INDENT_ELIF 0x0004 +#define INDENT_IFDEF 0x0008 +#define INDENT_IFNDEF 0x0010 + +//macro definitions +typedef struct define_s +{ + char *name; //define name + int flags; //define flags + int builtin; // > 0 if builtin define + int numparms; //number of define parameters + token_t *parms; //define parameters + token_t *tokens; //macro tokens (possibly containing parm tokens) + struct define_s *next; //next defined macro in a list + struct define_s *hashnext; //next define in the hash chain +} define_t; + +//indents +//used for conditional compilation directives: +//#if, #else, #elif, #ifdef, #ifndef +typedef struct indent_s +{ + int type; //indent type + int skip; //true if skipping current indent + script_t *script; //script the indent was in + struct indent_s *next; //next indent on the indent stack +} indent_t; + +//source file +typedef struct source_s +{ + char filename[1024]; //file name of the script + char includepath[1024]; //path to include files + punctuation_t *punctuations; //punctuations to use + script_t *scriptstack; //stack with scripts of the source + token_t *tokens; //tokens to read first + define_t *defines; //list with macro definitions + define_t **definehash; //hash chain with defines + indent_t *indentstack; //stack with indents + int skip; // > 0 if skipping conditional code + token_t token; //last read token +} source_t; + + +//read a token from the source +int PC_ReadToken(source_t *source, token_t *token); +//expect a certain token +int PC_ExpectTokenString(source_t *source, char *string); +//expect a certain token type +int PC_ExpectTokenType(source_t *source, int type, int subtype, token_t *token); +//expect a token +int PC_ExpectAnyToken(source_t *source, token_t *token); +//returns true when the token is available +int PC_CheckTokenString(source_t *source, char *string); +//returns true an reads the token when a token with the given type is available +int PC_CheckTokenType(source_t *source, int type, int subtype, token_t *token); +//skip tokens until the given token string is read +int PC_SkipUntilString(source_t *source, char *string); +//unread the last token read from the script +void PC_UnreadLastToken(source_t *source); +//unread the given token +void PC_UnreadToken(source_t *source, token_t *token); +//read a token only if on the same line, lines are concatenated with a slash +int PC_ReadLine(source_t *source, token_t *token); +//returns true if there was a white space in front of the token +int PC_WhiteSpaceBeforeToken(token_t *token); +//add a define to the source +int PC_AddDefine(source_t *source, char *string); +//add a globals define that will be added to all opened sources +int PC_AddGlobalDefine(char *string); +//remove the given global define +int PC_RemoveGlobalDefine(char *name); +//remove all globals defines +void PC_RemoveAllGlobalDefines(void); +//add builtin defines +void PC_AddBuiltinDefines(source_t *source); +//set the source include path +void PC_SetIncludePath(source_t *source, char *path); +//set the punction set +void PC_SetPunctuations(source_t *source, punctuation_t *p); +//set the base folder to load files from +void PC_SetBaseFolder(char *path); +//load a source file +source_t *LoadSourceFile(const char *filename); +//load a source from memory +source_t *LoadSourceMemory(char *ptr, int length, char *name); +//free the given source +void FreeSource(source_t *source); +//print a source error +void QDECL SourceError(source_t *source, char *str, ...); +//print a source warning +void QDECL SourceWarning(source_t *source, char *str, ...); + +#ifdef BSPC +// some of BSPC source does include game/q_shared.h and some does not +// we define pc_token_s pc_token_t if needed (yes, it's ugly) +#ifndef __Q_SHARED_H +#define MAX_TOKENLENGTH 1024 +typedef struct pc_token_s +{ + int type; + int subtype; + int intvalue; + float floatvalue; + char string[MAX_TOKENLENGTH]; +} pc_token_t; +#endif //!_Q_SHARED_H +#endif //BSPC + +// +int PC_LoadSourceHandle(const char *filename); +int PC_FreeSourceHandle(int handle); +int PC_ReadTokenHandle(int handle, pc_token_t *pc_token); +int PC_SourceFileAndLine(int handle, char *filename, int *line); +void PC_CheckOpenSourceHandles(void); diff --git a/reaction/engine/code/botlib/l_script.c b/reaction/engine/code/botlib/l_script.c new file mode 100644 index 00000000..c92481c0 --- /dev/null +++ b/reaction/engine/code/botlib/l_script.c @@ -0,0 +1,1431 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_script.c + * + * desc: lexicographical parser + * + * $Archive: /MissionPack/code/botlib/l_script.c $ + * + *****************************************************************************/ + +//#define SCREWUP +//#define BOTLIB +//#define MEQCC +//#define BSPC + +#ifdef SCREWUP +#include +#include +#include +#include +#include +#include "l_memory.h" +#include "l_script.h" + +typedef enum {qfalse, qtrue} qboolean; + +#endif //SCREWUP + +#ifdef BOTLIB +//include files for usage in the bot library +#include "../qcommon/q_shared.h" +#include "botlib.h" +#include "be_interface.h" +#include "l_script.h" +#include "l_memory.h" +#include "l_log.h" +#include "l_libvar.h" +#endif //BOTLIB + +#ifdef MEQCC +//include files for usage in MrElusive's QuakeC Compiler +#include "qcc.h" +#include "l_script.h" +#include "l_memory.h" +#include "l_log.h" + +#define qtrue true +#define qfalse false +#endif //MEQCC + +#ifdef BSPC +//include files for usage in the BSP Converter +#include "../bspc/qbsp.h" +#include "../bspc/l_log.h" +#include "../bspc/l_mem.h" + +#define qtrue true +#define qfalse false +#endif //BSPC + + +#define PUNCTABLE + +//longer punctuations first +punctuation_t default_punctuations[] = +{ + //binary operators + {">>=",P_RSHIFT_ASSIGN, NULL}, + {"<<=",P_LSHIFT_ASSIGN, NULL}, + // + {"...",P_PARMS, NULL}, + //define merge operator + {"##",P_PRECOMPMERGE, NULL}, + //logic operators + {"&&",P_LOGIC_AND, NULL}, + {"||",P_LOGIC_OR, NULL}, + {">=",P_LOGIC_GEQ, NULL}, + {"<=",P_LOGIC_LEQ, NULL}, + {"==",P_LOGIC_EQ, NULL}, + {"!=",P_LOGIC_UNEQ, NULL}, + //arithmatic operators + {"*=",P_MUL_ASSIGN, NULL}, + {"/=",P_DIV_ASSIGN, NULL}, + {"%=",P_MOD_ASSIGN, NULL}, + {"+=",P_ADD_ASSIGN, NULL}, + {"-=",P_SUB_ASSIGN, NULL}, + {"++",P_INC, NULL}, + {"--",P_DEC, NULL}, + //binary operators + {"&=",P_BIN_AND_ASSIGN, NULL}, + {"|=",P_BIN_OR_ASSIGN, NULL}, + {"^=",P_BIN_XOR_ASSIGN, NULL}, + {">>",P_RSHIFT, NULL}, + {"<<",P_LSHIFT, NULL}, + //reference operators + {"->",P_POINTERREF, NULL}, + //C++ + {"::",P_CPP1, NULL}, + {".*",P_CPP2, NULL}, + //arithmatic operators + {"*",P_MUL, NULL}, + {"/",P_DIV, NULL}, + {"%",P_MOD, NULL}, + {"+",P_ADD, NULL}, + {"-",P_SUB, NULL}, + {"=",P_ASSIGN, NULL}, + //binary operators + {"&",P_BIN_AND, NULL}, + {"|",P_BIN_OR, NULL}, + {"^",P_BIN_XOR, NULL}, + {"~",P_BIN_NOT, NULL}, + //logic operators + {"!",P_LOGIC_NOT, NULL}, + {">",P_LOGIC_GREATER, NULL}, + {"<",P_LOGIC_LESS, NULL}, + //reference operator + {".",P_REF, NULL}, + //seperators + {",",P_COMMA, NULL}, + {";",P_SEMICOLON, NULL}, + //label indication + {":",P_COLON, NULL}, + //if statement + {"?",P_QUESTIONMARK, NULL}, + //embracements + {"(",P_PARENTHESESOPEN, NULL}, + {")",P_PARENTHESESCLOSE, NULL}, + {"{",P_BRACEOPEN, NULL}, + {"}",P_BRACECLOSE, NULL}, + {"[",P_SQBRACKETOPEN, NULL}, + {"]",P_SQBRACKETCLOSE, NULL}, + // + {"\\",P_BACKSLASH, NULL}, + //precompiler operator + {"#",P_PRECOMP, NULL}, +#ifdef DOLLAR + {"$",P_DOLLAR, NULL}, +#endif //DOLLAR + {NULL, 0} +}; + +#ifdef BSPC +char basefolder[MAX_PATH]; +#else +char basefolder[MAX_QPATH]; +#endif + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PS_CreatePunctuationTable(script_t *script, punctuation_t *punctuations) +{ + int i; + punctuation_t *p, *lastp, *newp; + + //get memory for the table + if (!script->punctuationtable) script->punctuationtable = (punctuation_t **) + GetMemory(256 * sizeof(punctuation_t *)); + Com_Memset(script->punctuationtable, 0, 256 * sizeof(punctuation_t *)); + //add the punctuations in the list to the punctuation table + for (i = 0; punctuations[i].p; i++) + { + newp = &punctuations[i]; + lastp = NULL; + //sort the punctuations in this table entry on length (longer punctuations first) + for (p = script->punctuationtable[(unsigned int) newp->p[0]]; p; p = p->next) + { + if (strlen(p->p) < strlen(newp->p)) + { + newp->next = p; + if (lastp) lastp->next = newp; + else script->punctuationtable[(unsigned int) newp->p[0]] = newp; + break; + } //end if + lastp = p; + } //end for + if (!p) + { + newp->next = NULL; + if (lastp) lastp->next = newp; + else script->punctuationtable[(unsigned int) newp->p[0]] = newp; + } //end if + } //end for +} //end of the function PS_CreatePunctuationTable +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *PunctuationFromNum(script_t *script, int num) +{ + int i; + + for (i = 0; script->punctuations[i].p; i++) + { + if (script->punctuations[i].n == num) return script->punctuations[i].p; + } //end for + return "unkown punctuation"; +} //end of the function PunctuationFromNum +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void QDECL ScriptError(script_t *script, char *str, ...) +{ + char text[1024]; + va_list ap; + + if (script->flags & SCFL_NOERRORS) return; + + va_start(ap, str); + Q_vsnprintf(text, sizeof(text), str, ap); + va_end(ap); +#ifdef BOTLIB + botimport.Print(PRT_ERROR, "file %s, line %d: %s\n", script->filename, script->line, text); +#endif //BOTLIB +#ifdef MEQCC + printf("error: file %s, line %d: %s\n", script->filename, script->line, text); +#endif //MEQCC +#ifdef BSPC + Log_Print("error: file %s, line %d: %s\n", script->filename, script->line, text); +#endif //BSPC +} //end of the function ScriptError +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void QDECL ScriptWarning(script_t *script, char *str, ...) +{ + char text[1024]; + va_list ap; + + if (script->flags & SCFL_NOWARNINGS) return; + + va_start(ap, str); + Q_vsnprintf(text, sizeof(text), str, ap); + va_end(ap); +#ifdef BOTLIB + botimport.Print(PRT_WARNING, "file %s, line %d: %s\n", script->filename, script->line, text); +#endif //BOTLIB +#ifdef MEQCC + printf("warning: file %s, line %d: %s\n", script->filename, script->line, text); +#endif //MEQCC +#ifdef BSPC + Log_Print("warning: file %s, line %d: %s\n", script->filename, script->line, text); +#endif //BSPC +} //end of the function ScriptWarning +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void SetScriptPunctuations(script_t *script, punctuation_t *p) +{ +#ifdef PUNCTABLE + if (p) PS_CreatePunctuationTable(script, p); + else PS_CreatePunctuationTable(script, default_punctuations); +#endif //PUNCTABLE + if (p) script->punctuations = p; + else script->punctuations = default_punctuations; +} //end of the function SetScriptPunctuations +//============================================================================ +// Reads spaces, tabs, C-like comments etc. +// When a newline character is found the scripts line counter is increased. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadWhiteSpace(script_t *script) +{ + while(1) + { + //skip white space + while(*script->script_p <= ' ') + { + if (!*script->script_p) return 0; + if (*script->script_p == '\n') script->line++; + script->script_p++; + } //end while + //skip comments + if (*script->script_p == '/') + { + //comments // + if (*(script->script_p+1) == '/') + { + script->script_p++; + do + { + script->script_p++; + if (!*script->script_p) return 0; + } //end do + while(*script->script_p != '\n'); + script->line++; + script->script_p++; + if (!*script->script_p) return 0; + continue; + } //end if + //comments /* */ + else if (*(script->script_p+1) == '*') + { + script->script_p++; + do + { + script->script_p++; + if (!*script->script_p) return 0; + if (*script->script_p == '\n') script->line++; + } //end do + while(!(*script->script_p == '*' && *(script->script_p+1) == '/')); + script->script_p++; + if (!*script->script_p) return 0; + script->script_p++; + if (!*script->script_p) return 0; + continue; + } //end if + } //end if + break; + } //end while + return 1; +} //end of the function PS_ReadWhiteSpace +//============================================================================ +// Reads an escape character. +// +// Parameter: script : script to read from +// ch : place to store the read escape character +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadEscapeCharacter(script_t *script, char *ch) +{ + int c, val, i; + + //step over the leading '\\' + script->script_p++; + //determine the escape character + switch(*script->script_p) + { + case '\\': c = '\\'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'a': c = '\a'; break; + case '\'': c = '\''; break; + case '\"': c = '\"'; break; + case '\?': c = '\?'; break; + case 'x': + { + script->script_p++; + for (i = 0, val = 0; ; i++, script->script_p++) + { + c = *script->script_p; + if (c >= '0' && c <= '9') c = c - '0'; + else if (c >= 'A' && c <= 'Z') c = c - 'A' + 10; + else if (c >= 'a' && c <= 'z') c = c - 'a' + 10; + else break; + val = (val << 4) + c; + } //end for + script->script_p--; + if (val > 0xFF) + { + ScriptWarning(script, "too large value in escape character"); + val = 0xFF; + } //end if + c = val; + break; + } //end case + default: //NOTE: decimal ASCII code, NOT octal + { + if (*script->script_p < '0' || *script->script_p > '9') ScriptError(script, "unknown escape char"); + for (i = 0, val = 0; ; i++, script->script_p++) + { + c = *script->script_p; + if (c >= '0' && c <= '9') c = c - '0'; + else break; + val = val * 10 + c; + } //end for + script->script_p--; + if (val > 0xFF) + { + ScriptWarning(script, "too large value in escape character"); + val = 0xFF; + } //end if + c = val; + break; + } //end default + } //end switch + //step over the escape character or the last digit of the number + script->script_p++; + //store the escape character + *ch = c; + //succesfully read escape character + return 1; +} //end of the function PS_ReadEscapeCharacter +//============================================================================ +// Reads C-like string. Escape characters are interpretted. +// Quotes are included with the string. +// Reads two strings with a white space between them as one string. +// +// Parameter: script : script to read from +// token : buffer to store the string +// Returns: qtrue when a string was read succesfully +// Changes Globals: - +//============================================================================ +int PS_ReadString(script_t *script, token_t *token, int quote) +{ + int len, tmpline; + char *tmpscript_p; + + if (quote == '\"') token->type = TT_STRING; + else token->type = TT_LITERAL; + + len = 0; + //leading quote + token->string[len++] = *script->script_p++; + // + while(1) + { + //minus 2 because trailing double quote and zero have to be appended + if (len >= MAX_TOKEN - 2) + { + ScriptError(script, "string longer than MAX_TOKEN = %d", MAX_TOKEN); + return 0; + } //end if + //if there is an escape character and + //if escape characters inside a string are allowed + if (*script->script_p == '\\' && !(script->flags & SCFL_NOSTRINGESCAPECHARS)) + { + if (!PS_ReadEscapeCharacter(script, &token->string[len])) + { + token->string[len] = 0; + return 0; + } //end if + len++; + } //end if + //if a trailing quote + else if (*script->script_p == quote) + { + //step over the double quote + script->script_p++; + //if white spaces in a string are not allowed + if (script->flags & SCFL_NOSTRINGWHITESPACES) break; + // + tmpscript_p = script->script_p; + tmpline = script->line; + //read unusefull stuff between possible two following strings + if (!PS_ReadWhiteSpace(script)) + { + script->script_p = tmpscript_p; + script->line = tmpline; + break; + } //end if + //if there's no leading double qoute + if (*script->script_p != quote) + { + script->script_p = tmpscript_p; + script->line = tmpline; + break; + } //end if + //step over the new leading double quote + script->script_p++; + } //end if + else + { + if (*script->script_p == '\0') + { + token->string[len] = 0; + ScriptError(script, "missing trailing quote"); + return 0; + } //end if + if (*script->script_p == '\n') + { + token->string[len] = 0; + ScriptError(script, "newline inside string %s", token->string); + return 0; + } //end if + token->string[len++] = *script->script_p++; + } //end else + } //end while + //trailing quote + token->string[len++] = quote; + //end string with a zero + token->string[len] = '\0'; + //the sub type is the length of the string + token->subtype = len; + return 1; +} //end of the function PS_ReadString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadName(script_t *script, token_t *token) +{ + int len = 0; + char c; + + token->type = TT_NAME; + do + { + token->string[len++] = *script->script_p++; + if (len >= MAX_TOKEN) + { + ScriptError(script, "name longer than MAX_TOKEN = %d", MAX_TOKEN); + return 0; + } //end if + c = *script->script_p; + } while ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '_'); + token->string[len] = '\0'; + //the sub type is the length of the name + token->subtype = len; + return 1; +} //end of the function PS_ReadName +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void NumberValue(char *string, int subtype, unsigned long int *intvalue, + float *floatvalue) +{ + unsigned long int dotfound = 0; + + *intvalue = 0; + *floatvalue = 0; + //floating point number + if (subtype & TT_FLOAT) + { + while(*string) + { + if (*string == '.') + { + if (dotfound) return; + dotfound = 10; + string++; + } //end if + if (dotfound) + { + *floatvalue = *floatvalue + (float) (*string - '0') / + (float) dotfound; + dotfound *= 10; + } //end if + else + { + *floatvalue = *floatvalue * 10.0 + (float) (*string - '0'); + } //end else + string++; + } //end while + *intvalue = (unsigned long) *floatvalue; + } //end if + else if (subtype & TT_DECIMAL) + { + while(*string) *intvalue = *intvalue * 10 + (*string++ - '0'); + *floatvalue = *intvalue; + } //end else if + else if (subtype & TT_HEX) + { + //step over the leading 0x or 0X + string += 2; + while(*string) + { + *intvalue <<= 4; + if (*string >= 'a' && *string <= 'f') *intvalue += *string - 'a' + 10; + else if (*string >= 'A' && *string <= 'F') *intvalue += *string - 'A' + 10; + else *intvalue += *string - '0'; + string++; + } //end while + *floatvalue = *intvalue; + } //end else if + else if (subtype & TT_OCTAL) + { + //step over the first zero + string += 1; + while(*string) *intvalue = (*intvalue << 3) + (*string++ - '0'); + *floatvalue = *intvalue; + } //end else if + else if (subtype & TT_BINARY) + { + //step over the leading 0b or 0B + string += 2; + while(*string) *intvalue = (*intvalue << 1) + (*string++ - '0'); + *floatvalue = *intvalue; + } //end else if +} //end of the function NumberValue +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadNumber(script_t *script, token_t *token) +{ + int len = 0, i; + int octal, dot; + char c; +// unsigned long int intvalue = 0; +// double floatvalue = 0; + + token->type = TT_NUMBER; + //check for a hexadecimal number + if (*script->script_p == '0' && + (*(script->script_p + 1) == 'x' || + *(script->script_p + 1) == 'X')) + { + token->string[len++] = *script->script_p++; + token->string[len++] = *script->script_p++; + c = *script->script_p; + //hexadecimal + while((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'A')) + { + token->string[len++] = *script->script_p++; + if (len >= MAX_TOKEN) + { + ScriptError(script, "hexadecimal number longer than MAX_TOKEN = %d", MAX_TOKEN); + return 0; + } //end if + c = *script->script_p; + } //end while + token->subtype |= TT_HEX; + } //end if +#ifdef BINARYNUMBERS + //check for a binary number + else if (*script->script_p == '0' && + (*(script->script_p + 1) == 'b' || + *(script->script_p + 1) == 'B')) + { + token->string[len++] = *script->script_p++; + token->string[len++] = *script->script_p++; + c = *script->script_p; + //binary + while(c == '0' || c == '1') + { + token->string[len++] = *script->script_p++; + if (len >= MAX_TOKEN) + { + ScriptError(script, "binary number longer than MAX_TOKEN = %d", MAX_TOKEN); + return 0; + } //end if + c = *script->script_p; + } //end while + token->subtype |= TT_BINARY; + } //end if +#endif //BINARYNUMBERS + else //decimal or octal integer or floating point number + { + octal = qfalse; + dot = qfalse; + if (*script->script_p == '0') octal = qtrue; + while(1) + { + c = *script->script_p; + if (c == '.') dot = qtrue; + else if (c == '8' || c == '9') octal = qfalse; + else if (c < '0' || c > '9') break; + token->string[len++] = *script->script_p++; + if (len >= MAX_TOKEN - 1) + { + ScriptError(script, "number longer than MAX_TOKEN = %d", MAX_TOKEN); + return 0; + } //end if + } //end while + if (octal) token->subtype |= TT_OCTAL; + else token->subtype |= TT_DECIMAL; + if (dot) token->subtype |= TT_FLOAT; + } //end else + for (i = 0; i < 2; i++) + { + c = *script->script_p; + //check for a LONG number + if ( (c == 'l' || c == 'L') + && !(token->subtype & TT_LONG)) + { + script->script_p++; + token->subtype |= TT_LONG; + } //end if + //check for an UNSIGNED number + else if ( (c == 'u' || c == 'U') + && !(token->subtype & (TT_UNSIGNED | TT_FLOAT))) + { + script->script_p++; + token->subtype |= TT_UNSIGNED; + } //end if + } //end for + token->string[len] = '\0'; +#ifdef NUMBERVALUE + NumberValue(token->string, token->subtype, &token->intvalue, &token->floatvalue); +#endif //NUMBERVALUE + if (!(token->subtype & TT_FLOAT)) token->subtype |= TT_INTEGER; + return 1; +} //end of the function PS_ReadNumber +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadLiteral(script_t *script, token_t *token) +{ + token->type = TT_LITERAL; + //first quote + token->string[0] = *script->script_p++; + //check for end of file + if (!*script->script_p) + { + ScriptError(script, "end of file before trailing \'"); + return 0; + } //end if + //if it is an escape character + if (*script->script_p == '\\') + { + if (!PS_ReadEscapeCharacter(script, &token->string[1])) return 0; + } //end if + else + { + token->string[1] = *script->script_p++; + } //end else + //check for trailing quote + if (*script->script_p != '\'') + { + ScriptWarning(script, "too many characters in literal, ignored"); + while(*script->script_p && + *script->script_p != '\'' && + *script->script_p != '\n') + { + script->script_p++; + } //end while + if (*script->script_p == '\'') script->script_p++; + } //end if + //store the trailing quote + token->string[2] = *script->script_p++; + //store trailing zero to end the string + token->string[3] = '\0'; + //the sub type is the integer literal value + token->subtype = token->string[1]; + // + return 1; +} //end of the function PS_ReadLiteral +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadPunctuation(script_t *script, token_t *token) +{ + int len; + char *p; + punctuation_t *punc; + +#ifdef PUNCTABLE + for (punc = script->punctuationtable[(unsigned int)*script->script_p]; punc; punc = punc->next) + { +#else + int i; + + for (i = 0; script->punctuations[i].p; i++) + { + punc = &script->punctuations[i]; +#endif //PUNCTABLE + p = punc->p; + len = strlen(p); + //if the script contains at least as much characters as the punctuation + if (script->script_p + len <= script->end_p) + { + //if the script contains the punctuation + if (!strncmp(script->script_p, p, len)) + { + strncpy(token->string, p, MAX_TOKEN); + script->script_p += len; + token->type = TT_PUNCTUATION; + //sub type is the number of the punctuation + token->subtype = punc->n; + return 1; + } //end if + } //end if + } //end for + return 0; +} //end of the function PS_ReadPunctuation +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadPrimitive(script_t *script, token_t *token) +{ + int len; + + len = 0; + while(*script->script_p > ' ' && *script->script_p != ';') + { + if (len >= MAX_TOKEN) + { + ScriptError(script, "primitive token longer than MAX_TOKEN = %d", MAX_TOKEN); + return 0; + } //end if + token->string[len++] = *script->script_p++; + } //end while + token->string[len] = 0; + //copy the token into the script structure + Com_Memcpy(&script->token, token, sizeof(token_t)); + //primitive reading successfull + return 1; +} //end of the function PS_ReadPrimitive +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadToken(script_t *script, token_t *token) +{ + //if there is a token available (from UnreadToken) + if (script->tokenavailable) + { + script->tokenavailable = 0; + Com_Memcpy(token, &script->token, sizeof(token_t)); + return 1; + } //end if + //save script pointer + script->lastscript_p = script->script_p; + //save line counter + script->lastline = script->line; + //clear the token stuff + Com_Memset(token, 0, sizeof(token_t)); + //start of the white space + script->whitespace_p = script->script_p; + token->whitespace_p = script->script_p; + //read unusefull stuff + if (!PS_ReadWhiteSpace(script)) return 0; + //end of the white space + script->endwhitespace_p = script->script_p; + token->endwhitespace_p = script->script_p; + //line the token is on + token->line = script->line; + //number of lines crossed before token + token->linescrossed = script->line - script->lastline; + //if there is a leading double quote + if (*script->script_p == '\"') + { + if (!PS_ReadString(script, token, '\"')) return 0; + } //end if + //if an literal + else if (*script->script_p == '\'') + { + //if (!PS_ReadLiteral(script, token)) return 0; + if (!PS_ReadString(script, token, '\'')) return 0; + } //end if + //if there is a number + else if ((*script->script_p >= '0' && *script->script_p <= '9') || + (*script->script_p == '.' && + (*(script->script_p + 1) >= '0' && *(script->script_p + 1) <= '9'))) + { + if (!PS_ReadNumber(script, token)) return 0; + } //end if + //if this is a primitive script + else if (script->flags & SCFL_PRIMITIVE) + { + return PS_ReadPrimitive(script, token); + } //end else if + //if there is a name + else if ((*script->script_p >= 'a' && *script->script_p <= 'z') || + (*script->script_p >= 'A' && *script->script_p <= 'Z') || + *script->script_p == '_') + { + if (!PS_ReadName(script, token)) return 0; + } //end if + //check for punctuations + else if (!PS_ReadPunctuation(script, token)) + { + ScriptError(script, "can't read token"); + return 0; + } //end if + //copy the token into the script structure + Com_Memcpy(&script->token, token, sizeof(token_t)); + //succesfully read a token + return 1; +} //end of the function PS_ReadToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ExpectTokenString(script_t *script, char *string) +{ + token_t token; + + if (!PS_ReadToken(script, &token)) + { + ScriptError(script, "couldn't find expected %s", string); + return 0; + } //end if + + if (strcmp(token.string, string)) + { + ScriptError(script, "expected %s, found %s", string, token.string); + return 0; + } //end if + return 1; +} //end of the function PS_ExpectToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ExpectTokenType(script_t *script, int type, int subtype, token_t *token) +{ + char str[MAX_TOKEN]; + + if (!PS_ReadToken(script, token)) + { + ScriptError(script, "couldn't read expected token"); + return 0; + } //end if + + if (token->type != type) + { + if (type == TT_STRING) strcpy(str, "string"); + if (type == TT_LITERAL) strcpy(str, "literal"); + if (type == TT_NUMBER) strcpy(str, "number"); + if (type == TT_NAME) strcpy(str, "name"); + if (type == TT_PUNCTUATION) strcpy(str, "punctuation"); + ScriptError(script, "expected a %s, found %s", str, token->string); + return 0; + } //end if + if (token->type == TT_NUMBER) + { + if ((token->subtype & subtype) != subtype) + { + if (subtype & TT_DECIMAL) strcpy(str, "decimal"); + if (subtype & TT_HEX) strcpy(str, "hex"); + if (subtype & TT_OCTAL) strcpy(str, "octal"); + if (subtype & TT_BINARY) strcpy(str, "binary"); + if (subtype & TT_LONG) strcat(str, " long"); + if (subtype & TT_UNSIGNED) strcat(str, " unsigned"); + if (subtype & TT_FLOAT) strcat(str, " float"); + if (subtype & TT_INTEGER) strcat(str, " integer"); + ScriptError(script, "expected %s, found %s", str, token->string); + return 0; + } //end if + } //end if + else if (token->type == TT_PUNCTUATION) + { + if (subtype < 0) + { + ScriptError(script, "BUG: wrong punctuation subtype"); + return 0; + } //end if + if (token->subtype != subtype) + { + ScriptError(script, "expected %s, found %s", + script->punctuations[subtype], token->string); + return 0; + } //end if + } //end else if + return 1; +} //end of the function PS_ExpectTokenType +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ExpectAnyToken(script_t *script, token_t *token) +{ + if (!PS_ReadToken(script, token)) + { + ScriptError(script, "couldn't read expected token"); + return 0; + } //end if + else + { + return 1; + } //end else +} //end of the function PS_ExpectAnyToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_CheckTokenString(script_t *script, char *string) +{ + token_t tok; + + if (!PS_ReadToken(script, &tok)) return 0; + //if the token is available + if (!strcmp(tok.string, string)) return 1; + //token not available + script->script_p = script->lastscript_p; + return 0; +} //end of the function PS_CheckTokenString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_CheckTokenType(script_t *script, int type, int subtype, token_t *token) +{ + token_t tok; + + if (!PS_ReadToken(script, &tok)) return 0; + //if the type matches + if (tok.type == type && + (tok.subtype & subtype) == subtype) + { + Com_Memcpy(token, &tok, sizeof(token_t)); + return 1; + } //end if + //token is not available + script->script_p = script->lastscript_p; + return 0; +} //end of the function PS_CheckTokenType +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_SkipUntilString(script_t *script, char *string) +{ + token_t token; + + while(PS_ReadToken(script, &token)) + { + if (!strcmp(token.string, string)) return 1; + } //end while + return 0; +} //end of the function PS_SkipUntilString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PS_UnreadLastToken(script_t *script) +{ + script->tokenavailable = 1; +} //end of the function UnreadLastToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PS_UnreadToken(script_t *script, token_t *token) +{ + Com_Memcpy(&script->token, token, sizeof(token_t)); + script->tokenavailable = 1; +} //end of the function UnreadToken +//============================================================================ +// returns the next character of the read white space, returns NULL if none +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +char PS_NextWhiteSpaceChar(script_t *script) +{ + if (script->whitespace_p != script->endwhitespace_p) + { + return *script->whitespace_p++; + } //end if + else + { + return 0; + } //end else +} //end of the function PS_NextWhiteSpaceChar +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void StripDoubleQuotes(char *string) +{ + if (*string == '\"') + { + strcpy(string, string+1); + } //end if + if (string[strlen(string)-1] == '\"') + { + string[strlen(string)-1] = '\0'; + } //end if +} //end of the function StripDoubleQuotes +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void StripSingleQuotes(char *string) +{ + if (*string == '\'') + { + strcpy(string, string+1); + } //end if + if (string[strlen(string)-1] == '\'') + { + string[strlen(string)-1] = '\0'; + } //end if +} //end of the function StripSingleQuotes +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +float ReadSignedFloat(script_t *script) +{ + token_t token; + float sign = 1.0; + + PS_ExpectAnyToken(script, &token); + if (!strcmp(token.string, "-")) + { + sign = -1.0; + PS_ExpectTokenType(script, TT_NUMBER, 0, &token); + } //end if + else if (token.type != TT_NUMBER) + { + ScriptError(script, "expected float value, found %s\n", token.string); + } //end else if + return sign * token.floatvalue; +} //end of the function ReadSignedFloat +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +signed long int ReadSignedInt(script_t *script) +{ + token_t token; + signed long int sign = 1; + + PS_ExpectAnyToken(script, &token); + if (!strcmp(token.string, "-")) + { + sign = -1; + PS_ExpectTokenType(script, TT_NUMBER, TT_INTEGER, &token); + } //end if + else if (token.type != TT_NUMBER || token.subtype == TT_FLOAT) + { + ScriptError(script, "expected integer value, found %s\n", token.string); + } //end else if + return sign * token.intvalue; +} //end of the function ReadSignedInt +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void SetScriptFlags(script_t *script, int flags) +{ + script->flags = flags; +} //end of the function SetScriptFlags +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int GetScriptFlags(script_t *script) +{ + return script->flags; +} //end of the function GetScriptFlags +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void ResetScript(script_t *script) +{ + //pointer in script buffer + script->script_p = script->buffer; + //pointer in script buffer before reading token + script->lastscript_p = script->buffer; + //begin of white space + script->whitespace_p = NULL; + //end of white space + script->endwhitespace_p = NULL; + //set if there's a token available in script->token + script->tokenavailable = 0; + // + script->line = 1; + script->lastline = 1; + //clear the saved token + Com_Memset(&script->token, 0, sizeof(token_t)); +} //end of the function ResetScript +//============================================================================ +// returns true if at the end of the script +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int EndOfScript(script_t *script) +{ + return script->script_p >= script->end_p; +} //end of the function EndOfScript +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int NumLinesCrossed(script_t *script) +{ + return script->line - script->lastline; +} //end of the function NumLinesCrossed +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int ScriptSkipTo(script_t *script, char *value) +{ + int len; + char firstchar; + + firstchar = *value; + len = strlen(value); + do + { + if (!PS_ReadWhiteSpace(script)) return 0; + if (*script->script_p == firstchar) + { + if (!strncmp(script->script_p, value, len)) + { + return 1; + } //end if + } //end if + script->script_p++; + } while(1); +} //end of the function ScriptSkipTo +#ifndef BOTLIB +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int FileLength(FILE *fp) +{ + int pos; + int end; + + pos = ftell(fp); + fseek(fp, 0, SEEK_END); + end = ftell(fp); + fseek(fp, pos, SEEK_SET); + + return end; +} //end of the function FileLength +#endif +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +script_t *LoadScriptFile(const char *filename) +{ +#ifdef BOTLIB + fileHandle_t fp; + char pathname[MAX_QPATH]; +#else + FILE *fp; +#endif + int length; + void *buffer; + script_t *script; + +#ifdef BOTLIB + if (strlen(basefolder)) + Com_sprintf(pathname, sizeof(pathname), "%s/%s", basefolder, filename); + else + Com_sprintf(pathname, sizeof(pathname), "%s", filename); + length = botimport.FS_FOpenFile( pathname, &fp, FS_READ ); + if (!fp) return NULL; +#else + fp = fopen(filename, "rb"); + if (!fp) return NULL; + + length = FileLength(fp); +#endif + + buffer = GetClearedMemory(sizeof(script_t) + length + 1); + script = (script_t *) buffer; + Com_Memset(script, 0, sizeof(script_t)); + strcpy(script->filename, filename); + script->buffer = (char *) buffer + sizeof(script_t); + script->buffer[length] = 0; + script->length = length; + //pointer in script buffer + script->script_p = script->buffer; + //pointer in script buffer before reading token + script->lastscript_p = script->buffer; + //pointer to end of script buffer + script->end_p = &script->buffer[length]; + //set if there's a token available in script->token + script->tokenavailable = 0; + // + script->line = 1; + script->lastline = 1; + // + SetScriptPunctuations(script, NULL); + // +#ifdef BOTLIB + botimport.FS_Read(script->buffer, length, fp); + botimport.FS_FCloseFile(fp); +#else + if (fread(script->buffer, length, 1, fp) != 1) + { + FreeMemory(buffer); + script = NULL; + } //end if + fclose(fp); +#endif + + return script; +} //end of the function LoadScriptFile +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +script_t *LoadScriptMemory(char *ptr, int length, char *name) +{ + void *buffer; + script_t *script; + + buffer = GetClearedMemory(sizeof(script_t) + length + 1); + script = (script_t *) buffer; + Com_Memset(script, 0, sizeof(script_t)); + strcpy(script->filename, name); + script->buffer = (char *) buffer + sizeof(script_t); + script->buffer[length] = 0; + script->length = length; + //pointer in script buffer + script->script_p = script->buffer; + //pointer in script buffer before reading token + script->lastscript_p = script->buffer; + //pointer to end of script buffer + script->end_p = &script->buffer[length]; + //set if there's a token available in script->token + script->tokenavailable = 0; + // + script->line = 1; + script->lastline = 1; + // + SetScriptPunctuations(script, NULL); + // + Com_Memcpy(script->buffer, ptr, length); + // + return script; +} //end of the function LoadScriptMemory +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void FreeScript(script_t *script) +{ +#ifdef PUNCTABLE + if (script->punctuationtable) FreeMemory(script->punctuationtable); +#endif //PUNCTABLE + FreeMemory(script); +} //end of the function FreeScript +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PS_SetBaseFolder(char *path) +{ +#ifdef BSPC + sprintf(basefolder, path); +#else + Com_sprintf(basefolder, sizeof(basefolder), "%s", path); +#endif +} //end of the function PS_SetBaseFolder diff --git a/reaction/engine/code/botlib/l_script.h b/reaction/engine/code/botlib/l_script.h new file mode 100644 index 00000000..ceb0469c --- /dev/null +++ b/reaction/engine/code/botlib/l_script.h @@ -0,0 +1,247 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_script.h + * + * desc: lexicographical parser + * + * $Archive: /source/code/botlib/l_script.h $ + * + *****************************************************************************/ + +//undef if binary numbers of the form 0b... or 0B... are not allowed +#define BINARYNUMBERS +//undef if not using the token.intvalue and token.floatvalue +#define NUMBERVALUE +//use dollar sign also as punctuation +#define DOLLAR + +//maximum token length +#define MAX_TOKEN 1024 + +#if defined(BSPC) && !defined(QDECL) +#define QDECL +#endif + + +//script flags +#define SCFL_NOERRORS 0x0001 +#define SCFL_NOWARNINGS 0x0002 +#define SCFL_NOSTRINGWHITESPACES 0x0004 +#define SCFL_NOSTRINGESCAPECHARS 0x0008 +#define SCFL_PRIMITIVE 0x0010 +#define SCFL_NOBINARYNUMBERS 0x0020 +#define SCFL_NONUMBERVALUES 0x0040 + +//token types +#define TT_STRING 1 // string +#define TT_LITERAL 2 // literal +#define TT_NUMBER 3 // number +#define TT_NAME 4 // name +#define TT_PUNCTUATION 5 // punctuation + +//string sub type +//--------------- +// the length of the string +//literal sub type +//---------------- +// the ASCII code of the literal +//number sub type +//--------------- +#define TT_DECIMAL 0x0008 // decimal number +#define TT_HEX 0x0100 // hexadecimal number +#define TT_OCTAL 0x0200 // octal number +#ifdef BINARYNUMBERS +#define TT_BINARY 0x0400 // binary number +#endif //BINARYNUMBERS +#define TT_FLOAT 0x0800 // floating point number +#define TT_INTEGER 0x1000 // integer number +#define TT_LONG 0x2000 // long number +#define TT_UNSIGNED 0x4000 // unsigned number +//punctuation sub type +//-------------------- +#define P_RSHIFT_ASSIGN 1 +#define P_LSHIFT_ASSIGN 2 +#define P_PARMS 3 +#define P_PRECOMPMERGE 4 + +#define P_LOGIC_AND 5 +#define P_LOGIC_OR 6 +#define P_LOGIC_GEQ 7 +#define P_LOGIC_LEQ 8 +#define P_LOGIC_EQ 9 +#define P_LOGIC_UNEQ 10 + +#define P_MUL_ASSIGN 11 +#define P_DIV_ASSIGN 12 +#define P_MOD_ASSIGN 13 +#define P_ADD_ASSIGN 14 +#define P_SUB_ASSIGN 15 +#define P_INC 16 +#define P_DEC 17 + +#define P_BIN_AND_ASSIGN 18 +#define P_BIN_OR_ASSIGN 19 +#define P_BIN_XOR_ASSIGN 20 +#define P_RSHIFT 21 +#define P_LSHIFT 22 + +#define P_POINTERREF 23 +#define P_CPP1 24 +#define P_CPP2 25 +#define P_MUL 26 +#define P_DIV 27 +#define P_MOD 28 +#define P_ADD 29 +#define P_SUB 30 +#define P_ASSIGN 31 + +#define P_BIN_AND 32 +#define P_BIN_OR 33 +#define P_BIN_XOR 34 +#define P_BIN_NOT 35 + +#define P_LOGIC_NOT 36 +#define P_LOGIC_GREATER 37 +#define P_LOGIC_LESS 38 + +#define P_REF 39 +#define P_COMMA 40 +#define P_SEMICOLON 41 +#define P_COLON 42 +#define P_QUESTIONMARK 43 + +#define P_PARENTHESESOPEN 44 +#define P_PARENTHESESCLOSE 45 +#define P_BRACEOPEN 46 +#define P_BRACECLOSE 47 +#define P_SQBRACKETOPEN 48 +#define P_SQBRACKETCLOSE 49 +#define P_BACKSLASH 50 + +#define P_PRECOMP 51 +#define P_DOLLAR 52 +//name sub type +//------------- +// the length of the name + +//punctuation +typedef struct punctuation_s +{ + char *p; //punctuation character(s) + int n; //punctuation indication + struct punctuation_s *next; //next punctuation +} punctuation_t; + +//token +typedef struct token_s +{ + char string[MAX_TOKEN]; //available token + int type; //last read token type + int subtype; //last read token sub type +#ifdef NUMBERVALUE + unsigned long int intvalue; //integer value + float floatvalue; //floating point value +#endif //NUMBERVALUE + char *whitespace_p; //start of white space before token + char *endwhitespace_p; //start of white space before token + int line; //line the token was on + int linescrossed; //lines crossed in white space + struct token_s *next; //next token in chain +} token_t; + +//script file +typedef struct script_s +{ + char filename[1024]; //file name of the script + char *buffer; //buffer containing the script + char *script_p; //current pointer in the script + char *end_p; //pointer to the end of the script + char *lastscript_p; //script pointer before reading token + char *whitespace_p; //begin of the white space + char *endwhitespace_p; //end of the white space + int length; //length of the script in bytes + int line; //current line in script + int lastline; //line before reading token + int tokenavailable; //set by UnreadLastToken + int flags; //several script flags + punctuation_t *punctuations; //the punctuations used in the script + punctuation_t **punctuationtable; + token_t token; //available token + struct script_s *next; //next script in a chain +} script_t; + +//read a token from the script +int PS_ReadToken(script_t *script, token_t *token); +//expect a certain token +int PS_ExpectTokenString(script_t *script, char *string); +//expect a certain token type +int PS_ExpectTokenType(script_t *script, int type, int subtype, token_t *token); +//expect a token +int PS_ExpectAnyToken(script_t *script, token_t *token); +//returns true when the token is available +int PS_CheckTokenString(script_t *script, char *string); +//returns true an reads the token when a token with the given type is available +int PS_CheckTokenType(script_t *script, int type, int subtype, token_t *token); +//skip tokens until the given token string is read +int PS_SkipUntilString(script_t *script, char *string); +//unread the last token read from the script +void PS_UnreadLastToken(script_t *script); +//unread the given token +void PS_UnreadToken(script_t *script, token_t *token); +//returns the next character of the read white space, returns NULL if none +char PS_NextWhiteSpaceChar(script_t *script); +//remove any leading and trailing double quotes from the token +void StripDoubleQuotes(char *string); +//remove any leading and trailing single quotes from the token +void StripSingleQuotes(char *string); +//read a possible signed integer +signed long int ReadSignedInt(script_t *script); +//read a possible signed floating point number +float ReadSignedFloat(script_t *script); +//set an array with punctuations, NULL restores default C/C++ set +void SetScriptPunctuations(script_t *script, punctuation_t *p); +//set script flags +void SetScriptFlags(script_t *script, int flags); +//get script flags +int GetScriptFlags(script_t *script); +//reset a script +void ResetScript(script_t *script); +//returns true if at the end of the script +int EndOfScript(script_t *script); +//returns a pointer to the punctuation with the given number +char *PunctuationFromNum(script_t *script, int num); +//load a script from the given file at the given offset with the given length +script_t *LoadScriptFile(const char *filename); +//load a script from the given memory with the given length +script_t *LoadScriptMemory(char *ptr, int length, char *name); +//free a script +void FreeScript(script_t *script); +//set the base folder to load files from +void PS_SetBaseFolder(char *path); +//print a script error with filename and line number +void QDECL ScriptError(script_t *script, char *str, ...); +//print a script warning with filename and line number +void QDECL ScriptWarning(script_t *script, char *str, ...); + + diff --git a/reaction/engine/code/botlib/l_struct.c b/reaction/engine/code/botlib/l_struct.c new file mode 100644 index 00000000..d948dc3c --- /dev/null +++ b/reaction/engine/code/botlib/l_struct.c @@ -0,0 +1,462 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_struct.c + * + * desc: structure reading / writing + * + * $Archive: /MissionPack/CODE/botlib/l_struct.c $ + * + *****************************************************************************/ + +#ifdef BOTLIB +#include "../qcommon/q_shared.h" +#include "botlib.h" //for the include of be_interface.h +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_utils.h" +#include "be_interface.h" +#endif //BOTLIB + +#ifdef BSPC +//include files for usage in the BSP Converter +#include "../bspc/qbsp.h" +#include "../bspc/l_log.h" +#include "../bspc/l_mem.h" +#include "l_precomp.h" +#include "l_struct.h" + +#define qtrue true +#define qfalse false +#endif //BSPC + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +fielddef_t *FindField(fielddef_t *defs, char *name) +{ + int i; + + for (i = 0; defs[i].name; i++) + { + if (!strcmp(defs[i].name, name)) return &defs[i]; + } //end for + return NULL; +} //end of the function FindField +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean ReadNumber(source_t *source, fielddef_t *fd, void *p) +{ + token_t token; + int negative = qfalse; + long int intval, intmin = 0, intmax = 0; + double floatval; + + if (!PC_ExpectAnyToken(source, &token)) return 0; + + //check for minus sign + if (token.type == TT_PUNCTUATION) + { + if (fd->type & FT_UNSIGNED) + { + SourceError(source, "expected unsigned value, found %s", token.string); + return 0; + } //end if + //if not a minus sign + if (strcmp(token.string, "-")) + { + SourceError(source, "unexpected punctuation %s", token.string); + return 0; + } //end if + negative = qtrue; + //read the number + if (!PC_ExpectAnyToken(source, &token)) return 0; + } //end if + //check if it is a number + if (token.type != TT_NUMBER) + { + SourceError(source, "expected number, found %s", token.string); + return 0; + } //end if + //check for a float value + if (token.subtype & TT_FLOAT) + { + if ((fd->type & FT_TYPE) != FT_FLOAT) + { + SourceError(source, "unexpected float"); + return 0; + } //end if + floatval = token.floatvalue; + if (negative) floatval = -floatval; + if (fd->type & FT_BOUNDED) + { + if (floatval < fd->floatmin || floatval > fd->floatmax) + { + SourceError(source, "float out of range [%f, %f]", fd->floatmin, fd->floatmax); + return 0; + } //end if + } //end if + *(float *) p = (float) floatval; + return 1; + } //end if + // + intval = token.intvalue; + if (negative) intval = -intval; + //check bounds + if ((fd->type & FT_TYPE) == FT_CHAR) + { + if (fd->type & FT_UNSIGNED) {intmin = 0; intmax = 255;} + else {intmin = -128; intmax = 127;} + } //end if + if ((fd->type & FT_TYPE) == FT_INT) + { + if (fd->type & FT_UNSIGNED) {intmin = 0; intmax = 65535;} + else {intmin = -32768; intmax = 32767;} + } //end else if + if ((fd->type & FT_TYPE) == FT_CHAR || (fd->type & FT_TYPE) == FT_INT) + { + if (fd->type & FT_BOUNDED) + { + intmin = Maximum(intmin, fd->floatmin); + intmax = Minimum(intmax, fd->floatmax); + } //end if + if (intval < intmin || intval > intmax) + { + SourceError(source, "value %d out of range [%d, %d]", intval, intmin, intmax); + return 0; + } //end if + } //end if + else if ((fd->type & FT_TYPE) == FT_FLOAT) + { + if (fd->type & FT_BOUNDED) + { + if (intval < fd->floatmin || intval > fd->floatmax) + { + SourceError(source, "value %d out of range [%f, %f]", intval, fd->floatmin, fd->floatmax); + return 0; + } //end if + } //end if + } //end else if + //store the value + if ((fd->type & FT_TYPE) == FT_CHAR) + { + if (fd->type & FT_UNSIGNED) *(unsigned char *) p = (unsigned char) intval; + else *(char *) p = (char) intval; + } //end if + else if ((fd->type & FT_TYPE) == FT_INT) + { + if (fd->type & FT_UNSIGNED) *(unsigned int *) p = (unsigned int) intval; + else *(int *) p = (int) intval; + } //end else + else if ((fd->type & FT_TYPE) == FT_FLOAT) + { + *(float *) p = (float) intval; + } //end else + return 1; +} //end of the function ReadNumber +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean ReadChar(source_t *source, fielddef_t *fd, void *p) +{ + token_t token; + + if (!PC_ExpectAnyToken(source, &token)) return 0; + + //take literals into account + if (token.type == TT_LITERAL) + { + StripSingleQuotes(token.string); + *(char *) p = token.string[0]; + } //end if + else + { + PC_UnreadLastToken(source); + if (!ReadNumber(source, fd, p)) return 0; + } //end if + return 1; +} //end of the function ReadChar +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int ReadString(source_t *source, fielddef_t *fd, void *p) +{ + token_t token; + + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) return 0; + //remove the double quotes + StripDoubleQuotes(token.string); + //copy the string + strncpy((char *) p, token.string, MAX_STRINGFIELD); + //make sure the string is closed with a zero + ((char *)p)[MAX_STRINGFIELD-1] = '\0'; + // + return 1; +} //end of the function ReadString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int ReadStructure(source_t *source, structdef_t *def, char *structure) +{ + token_t token; + fielddef_t *fd; + void *p; + int num; + + if (!PC_ExpectTokenString(source, "{")) return 0; + while(1) + { + if (!PC_ExpectAnyToken(source, &token)) return qfalse; + //if end of structure + if (!strcmp(token.string, "}")) break; + //find the field with the name + fd = FindField(def->fields, token.string); + if (!fd) + { + SourceError(source, "unknown structure field %s", token.string); + return qfalse; + } //end if + if (fd->type & FT_ARRAY) + { + num = fd->maxarray; + if (!PC_ExpectTokenString(source, "{")) return qfalse; + } //end if + else + { + num = 1; + } //end else + p = (void *)(structure + fd->offset); + while (num-- > 0) + { + if (fd->type & FT_ARRAY) + { + if (PC_CheckTokenString(source, "}")) break; + } //end if + switch(fd->type & FT_TYPE) + { + case FT_CHAR: + { + if (!ReadChar(source, fd, p)) return qfalse; + p = (char *) p + sizeof(char); + break; + } //end case + case FT_INT: + { + if (!ReadNumber(source, fd, p)) return qfalse; + p = (char *) p + sizeof(int); + break; + } //end case + case FT_FLOAT: + { + if (!ReadNumber(source, fd, p)) return qfalse; + p = (char *) p + sizeof(float); + break; + } //end case + case FT_STRING: + { + if (!ReadString(source, fd, p)) return qfalse; + p = (char *) p + MAX_STRINGFIELD; + break; + } //end case + case FT_STRUCT: + { + if (!fd->substruct) + { + SourceError(source, "BUG: no sub structure defined"); + return qfalse; + } //end if + ReadStructure(source, fd->substruct, (char *) p); + p = (char *) p + fd->substruct->size; + break; + } //end case + } //end switch + if (fd->type & FT_ARRAY) + { + if (!PC_ExpectAnyToken(source, &token)) return qfalse; + if (!strcmp(token.string, "}")) break; + if (strcmp(token.string, ",")) + { + SourceError(source, "expected a comma, found %s", token.string); + return qfalse; + } //end if + } //end if + } //end while + } //end while + return qtrue; +} //end of the function ReadStructure +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WriteIndent(FILE *fp, int indent) +{ + while(indent-- > 0) + { + if (fprintf(fp, "\t") < 0) return qfalse; + } //end while + return qtrue; +} //end of the function WriteIndent +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WriteFloat(FILE *fp, float value) +{ + char buf[128]; + int l; + + Com_sprintf(buf, sizeof(buf), "%f", value); + l = strlen(buf); + //strip any trailing zeros + while(l-- > 1) + { + if (buf[l] != '0' && buf[l] != '.') break; + if (buf[l] == '.') + { + buf[l] = 0; + break; + } //end if + buf[l] = 0; + } //end while + //write the float to file + if (fprintf(fp, "%s", buf) < 0) return 0; + return 1; +} //end of the function WriteFloat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WriteStructWithIndent(FILE *fp, structdef_t *def, char *structure, int indent) +{ + int i, num; + void *p; + fielddef_t *fd; + + if (!WriteIndent(fp, indent)) return qfalse; + if (fprintf(fp, "{\r\n") < 0) return qfalse; + + indent++; + for (i = 0; def->fields[i].name; i++) + { + fd = &def->fields[i]; + if (!WriteIndent(fp, indent)) return qfalse; + if (fprintf(fp, "%s\t", fd->name) < 0) return qfalse; + p = (void *)(structure + fd->offset); + if (fd->type & FT_ARRAY) + { + num = fd->maxarray; + if (fprintf(fp, "{") < 0) return qfalse; + } //end if + else + { + num = 1; + } //end else + while(num-- > 0) + { + switch(fd->type & FT_TYPE) + { + case FT_CHAR: + { + if (fprintf(fp, "%d", *(char *) p) < 0) return qfalse; + p = (char *) p + sizeof(char); + break; + } //end case + case FT_INT: + { + if (fprintf(fp, "%d", *(int *) p) < 0) return qfalse; + p = (char *) p + sizeof(int); + break; + } //end case + case FT_FLOAT: + { + if (!WriteFloat(fp, *(float *)p)) return qfalse; + p = (char *) p + sizeof(float); + break; + } //end case + case FT_STRING: + { + if (fprintf(fp, "\"%s\"", (char *) p) < 0) return qfalse; + p = (char *) p + MAX_STRINGFIELD; + break; + } //end case + case FT_STRUCT: + { + if (!WriteStructWithIndent(fp, fd->substruct, structure, indent)) return qfalse; + p = (char *) p + fd->substruct->size; + break; + } //end case + } //end switch + if (fd->type & FT_ARRAY) + { + if (num > 0) + { + if (fprintf(fp, ",") < 0) return qfalse; + } //end if + else + { + if (fprintf(fp, "}") < 0) return qfalse; + } //end else + } //end if + } //end while + if (fprintf(fp, "\r\n") < 0) return qfalse; + } //end for + indent--; + + if (!WriteIndent(fp, indent)) return qfalse; + if (fprintf(fp, "}\r\n") < 0) return qfalse; + return qtrue; +} //end of the function WriteStructWithIndent +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WriteStructure(FILE *fp, structdef_t *def, char *structure) +{ + return WriteStructWithIndent(fp, def, structure, 0); +} //end of the function WriteStructure + diff --git a/reaction/engine/code/botlib/l_struct.h b/reaction/engine/code/botlib/l_struct.h new file mode 100644 index 00000000..e2c6b032 --- /dev/null +++ b/reaction/engine/code/botlib/l_struct.h @@ -0,0 +1,75 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_struct.h + * + * desc: structure reading/writing + * + * $Archive: /source/code/botlib/l_struct.h $ + * + *****************************************************************************/ + + +#define MAX_STRINGFIELD 80 +//field types +#define FT_CHAR 1 // char +#define FT_INT 2 // int +#define FT_FLOAT 3 // float +#define FT_STRING 4 // char [MAX_STRINGFIELD] +#define FT_STRUCT 6 // struct (sub structure) +//type only mask +#define FT_TYPE 0x00FF // only type, clear subtype +//sub types +#define FT_ARRAY 0x0100 // array of type +#define FT_BOUNDED 0x0200 // bounded value +#define FT_UNSIGNED 0x0400 + +//structure field definition +typedef struct fielddef_s +{ + char *name; //name of the field + int offset; //offset in the structure + int type; //type of the field + //type specific fields + int maxarray; //maximum array size + float floatmin, floatmax; //float min and max + struct structdef_s *substruct; //sub structure +} fielddef_t; + +//structure definition +typedef struct structdef_s +{ + int size; + fielddef_t *fields; +} structdef_t; + +//read a structure from a script +int ReadStructure(source_t *source, structdef_t *def, char *structure); +//write a structure to a file +int WriteStructure(FILE *fp, structdef_t *def, char *structure); +//writes indents +int WriteIndent(FILE *fp, int indent); +//writes a float without traling zeros +int WriteFloat(FILE *fp, float value); + + diff --git a/reaction/engine/code/botlib/l_utils.h b/reaction/engine/code/botlib/l_utils.h new file mode 100644 index 00000000..6944d06f --- /dev/null +++ b/reaction/engine/code/botlib/l_utils.h @@ -0,0 +1,37 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_util.h + * + * desc: utils + * + * $Archive: /source/code/botlib/l_util.h $ + * + *****************************************************************************/ + +#define Vector2Angles(v,a) vectoangles(v,a) +#ifndef MAX_PATH +#define MAX_PATH MAX_QPATH +#endif +#define Maximum(x,y) (x > y ? x : y) +#define Minimum(x,y) (x < y ? x : y) diff --git a/reaction/engine/code/botlib/lcc.mak b/reaction/engine/code/botlib/lcc.mak new file mode 100644 index 00000000..f5567c3a --- /dev/null +++ b/reaction/engine/code/botlib/lcc.mak @@ -0,0 +1,55 @@ +# +# Makefile for Gladiator Bot library: gladiator.dll +# Intended for LCC-Win32 +# + +CC=lcc +CFLAGS=-DC_ONLY -o +OBJS= be_aas_bspq2.obj \ + be_aas_bsphl.obj \ + be_aas_cluster.obj \ + be_aas_debug.obj \ + be_aas_entity.obj \ + be_aas_file.obj \ + be_aas_light.obj \ + be_aas_main.obj \ + be_aas_move.obj \ + be_aas_optimize.obj \ + be_aas_reach.obj \ + be_aas_route.obj \ + be_aas_routealt.obj \ + be_aas_sample.obj \ + be_aas_sound.obj \ + be_ai2_dm.obj \ + be_ai2_dmnet.obj \ + be_ai2_main.obj \ + be_ai_char.obj \ + be_ai_chat.obj \ + be_ai_goal.obj \ + be_ai_load.obj \ + be_ai_move.obj \ + be_ai_weap.obj \ + be_ai_weight.obj \ + be_ea.obj \ + be_interface.obj \ + l_crc.obj \ + l_libvar.obj \ + l_log.obj \ + l_memory.obj \ + l_precomp.obj \ + l_script.obj \ + l_struct.obj \ + l_utils.obj \ + q_shared.obj + +all: gladiator.dll + +gladiator.dll: $(OBJS) + lcclnk -dll -entry GetBotAPI *.obj botlib.def -o gladiator.dll + +clean: + del *.obj gladiator.dll + +%.obj: %.c + $(CC) $(CFLAGS) $< + diff --git a/reaction/engine/code/botlib/linux-i386.mak b/reaction/engine/code/botlib/linux-i386.mak new file mode 100644 index 00000000..c9607a7b --- /dev/null +++ b/reaction/engine/code/botlib/linux-i386.mak @@ -0,0 +1,92 @@ +# +# Makefile for Gladiator Bot library: gladiator.so +# Intended for gcc/Linux +# + +ARCH=i386 +CC=gcc +BASE_CFLAGS=-Dstricmp=strcasecmp + +#use these cflags to optimize it +CFLAGS=$(BASE_CFLAGS) -m486 -O6 -ffast-math -funroll-loops \ + -fomit-frame-pointer -fexpensive-optimizations -malign-loops=2 \ + -malign-jumps=2 -malign-functions=2 +#use these when debugging +#CFLAGS=$(BASE_CFLAGS) -g + +LDFLAGS=-ldl -lm +SHLIBEXT=so +SHLIBCFLAGS=-fPIC +SHLIBLDFLAGS=-shared + +DO_CC=$(CC) $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< + +############################################################################# +# SETUP AND BUILD +# GLADIATOR BOT +############################################################################# + +.c.o: + $(DO_CC) + +GAME_OBJS = \ + be_aas_bsphl.o\ + be_aas_bspq2.o\ + be_aas_cluster.o\ + be_aas_debug.o\ + be_aas_entity.o\ + be_aas_file.o\ + be_aas_light.o\ + be_aas_main.o\ + be_aas_move.o\ + be_aas_optimize.o\ + be_aas_reach.o\ + be_aas_route.o\ + be_aas_routealt.o\ + be_aas_sample.o\ + be_aas_sound.o\ + be_ai2_dmq2.o\ + be_ai2_dmhl.o\ + be_ai2_dmnet.o\ + be_ai2_main.o\ + be_ai_char.o\ + be_ai_chat.o\ + be_ai_goal.o\ + be_ai_load.o\ + be_ai_move.o\ + be_ai_weap.o\ + be_ai_weight.o\ + be_ea.o\ + be_interface.o\ + l_crc.o\ + l_libvar.o\ + l_log.o\ + l_memory.o\ + l_precomp.o\ + l_script.o\ + l_struct.o\ + l_utils.o\ + q_shared.o + +glad$(ARCH).$(SHLIBEXT) : $(GAME_OBJS) + $(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(GAME_OBJS) + + +############################################################################# +# MISC +############################################################################# + +clean: + -rm -f $(GAME_OBJS) + +depend: + gcc -MM $(GAME_OBJS:.o=.c) + + +install: + cp gladiator.so .. + +# +# From "make depend" +# + diff --git a/reaction/engine/code/cgame/cg_consolecmds.c b/reaction/engine/code/cgame/cg_consolecmds.c new file mode 100644 index 00000000..c8f80190 --- /dev/null +++ b/reaction/engine/code/cgame/cg_consolecmds.c @@ -0,0 +1,578 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_consolecmds.c -- text commands typed in at the local console, or +// executed by a key binding + +#include "cg_local.h" +#include "../ui/ui_shared.h" +#ifdef MISSIONPACK +extern menuDef_t *menuScoreboard; +#endif + + + +void CG_TargetCommand_f( void ) { + int targetNum; + char test[4]; + + targetNum = CG_CrosshairPlayer(); + if (!targetNum ) { + return; + } + + trap_Argv( 1, test, 4 ); + trap_SendConsoleCommand( va( "gc %i %i", targetNum, atoi( test ) ) ); +} + + + +/* +================= +CG_SizeUp_f + +Keybinding command +================= +*/ +static void CG_SizeUp_f (void) { + trap_Cvar_Set("cg_viewsize", va("%i",(int)(cg_viewsize.integer+10))); +} + + +/* +================= +CG_SizeDown_f + +Keybinding command +================= +*/ +static void CG_SizeDown_f (void) { + trap_Cvar_Set("cg_viewsize", va("%i",(int)(cg_viewsize.integer-10))); +} + + +/* +============= +CG_Viewpos_f + +Debugging command to print the current position +============= +*/ +static void CG_Viewpos_f (void) { + CG_Printf ("(%i %i %i) : %i\n", (int)cg.refdef.vieworg[0], + (int)cg.refdef.vieworg[1], (int)cg.refdef.vieworg[2], + (int)cg.refdefViewAngles[YAW]); +} + + +static void CG_ScoresDown_f( void ) { + +#ifdef MISSIONPACK + CG_BuildSpectatorString(); +#endif + if ( cg.scoresRequestTime + 2000 < cg.time ) { + // the scores are more than two seconds out of data, + // so request new ones + cg.scoresRequestTime = cg.time; + trap_SendClientCommand( "score" ); + + // leave the current scores up if they were already + // displayed, but if this is the first hit, clear them out + if ( !cg.showScores ) { + cg.showScores = qtrue; + cg.numScores = 0; + } + } else { + // show the cached contents even if they just pressed if it + // is within two seconds + cg.showScores = qtrue; + } +} + +static void CG_ScoresUp_f( void ) { + if ( cg.showScores ) { + cg.showScores = qfalse; + cg.scoreFadeTime = cg.time; + } +} + +#ifdef MISSIONPACK +extern menuDef_t *menuScoreboard; +void Menu_Reset( void ); // FIXME: add to right include file + +static void CG_LoadHud_f( void) { + char buff[1024]; + const char *hudSet; + memset(buff, 0, sizeof(buff)); + + String_Init(); + Menu_Reset(); + + trap_Cvar_VariableStringBuffer("cg_hudFiles", buff, sizeof(buff)); + hudSet = buff; + if (hudSet[0] == '\0') { + hudSet = "ui/hud.txt"; + } + + CG_LoadMenus(hudSet); + menuScoreboard = NULL; +} + + +static void CG_scrollScoresDown_f( void) { + if (menuScoreboard && cg.scoreBoardShowing) { + Menu_ScrollFeeder(menuScoreboard, FEEDER_SCOREBOARD, qtrue); + Menu_ScrollFeeder(menuScoreboard, FEEDER_REDTEAM_LIST, qtrue); + Menu_ScrollFeeder(menuScoreboard, FEEDER_BLUETEAM_LIST, qtrue); + } +} + + +static void CG_scrollScoresUp_f( void) { + if (menuScoreboard && cg.scoreBoardShowing) { + Menu_ScrollFeeder(menuScoreboard, FEEDER_SCOREBOARD, qfalse); + Menu_ScrollFeeder(menuScoreboard, FEEDER_REDTEAM_LIST, qfalse); + Menu_ScrollFeeder(menuScoreboard, FEEDER_BLUETEAM_LIST, qfalse); + } +} + + +static void CG_spWin_f( void) { + trap_Cvar_Set("cg_cameraOrbit", "2"); + trap_Cvar_Set("cg_cameraOrbitDelay", "35"); + trap_Cvar_Set("cg_thirdPerson", "1"); + trap_Cvar_Set("cg_thirdPersonAngle", "0"); + trap_Cvar_Set("cg_thirdPersonRange", "100"); + CG_AddBufferedSound(cgs.media.winnerSound); + //trap_S_StartLocalSound(cgs.media.winnerSound, CHAN_ANNOUNCER); + CG_CenterPrint("YOU WIN!", SCREEN_HEIGHT * .30, 0); +} + +static void CG_spLose_f( void) { + trap_Cvar_Set("cg_cameraOrbit", "2"); + trap_Cvar_Set("cg_cameraOrbitDelay", "35"); + trap_Cvar_Set("cg_thirdPerson", "1"); + trap_Cvar_Set("cg_thirdPersonAngle", "0"); + trap_Cvar_Set("cg_thirdPersonRange", "100"); + CG_AddBufferedSound(cgs.media.loserSound); + //trap_S_StartLocalSound(cgs.media.loserSound, CHAN_ANNOUNCER); + CG_CenterPrint("YOU LOSE...", SCREEN_HEIGHT * .30, 0); +} + +#endif + +static void CG_TellTarget_f( void ) { + int clientNum; + char command[128]; + char message[128]; + + clientNum = CG_CrosshairPlayer(); + if ( clientNum == -1 ) { + return; + } + + trap_Args( message, 128 ); + Com_sprintf( command, 128, "tell %i %s", clientNum, message ); + trap_SendClientCommand( command ); +} + +static void CG_TellAttacker_f( void ) { + int clientNum; + char command[128]; + char message[128]; + + clientNum = CG_LastAttacker(); + if ( clientNum == -1 ) { + return; + } + + trap_Args( message, 128 ); + Com_sprintf( command, 128, "tell %i %s", clientNum, message ); + trap_SendClientCommand( command ); +} + +static void CG_VoiceTellTarget_f( void ) { + int clientNum; + char command[128]; + char message[128]; + + clientNum = CG_CrosshairPlayer(); + if ( clientNum == -1 ) { + return; + } + + trap_Args( message, 128 ); + Com_sprintf( command, 128, "vtell %i %s", clientNum, message ); + trap_SendClientCommand( command ); +} + +static void CG_VoiceTellAttacker_f( void ) { + int clientNum; + char command[128]; + char message[128]; + + clientNum = CG_LastAttacker(); + if ( clientNum == -1 ) { + return; + } + + trap_Args( message, 128 ); + Com_sprintf( command, 128, "vtell %i %s", clientNum, message ); + trap_SendClientCommand( command ); +} + +#ifdef MISSIONPACK +static void CG_NextTeamMember_f( void ) { + CG_SelectNextPlayer(); +} + +static void CG_PrevTeamMember_f( void ) { + CG_SelectPrevPlayer(); +} + +// ASS U ME's enumeration order as far as task specific orders, OFFENSE is zero, CAMP is last +// +static void CG_NextOrder_f( void ) { + clientInfo_t *ci = cgs.clientinfo + cg.snap->ps.clientNum; + if (ci) { + if (!ci->teamLeader && sortedTeamPlayers[cg_currentSelectedPlayer.integer] != cg.snap->ps.clientNum) { + return; + } + } + if (cgs.currentOrder < TEAMTASK_CAMP) { + cgs.currentOrder++; + + if (cgs.currentOrder == TEAMTASK_RETRIEVE) { + if (!CG_OtherTeamHasFlag()) { + cgs.currentOrder++; + } + } + + if (cgs.currentOrder == TEAMTASK_ESCORT) { + if (!CG_YourTeamHasFlag()) { + cgs.currentOrder++; + } + } + + } else { + cgs.currentOrder = TEAMTASK_OFFENSE; + } + cgs.orderPending = qtrue; + cgs.orderTime = cg.time + 3000; +} + + +static void CG_ConfirmOrder_f (void ) { + trap_SendConsoleCommand(va("cmd vtell %d %s\n", cgs.acceptLeader, VOICECHAT_YES)); + trap_SendConsoleCommand("+button5; wait; -button5"); + if (cg.time < cgs.acceptOrderTime) { + trap_SendClientCommand(va("teamtask %d\n", cgs.acceptTask)); + cgs.acceptOrderTime = 0; + } +} + +static void CG_DenyOrder_f (void ) { + trap_SendConsoleCommand(va("cmd vtell %d %s\n", cgs.acceptLeader, VOICECHAT_NO)); + trap_SendConsoleCommand("+button6; wait; -button6"); + if (cg.time < cgs.acceptOrderTime) { + cgs.acceptOrderTime = 0; + } +} + +static void CG_TaskOffense_f (void ) { + if (cgs.gametype == GT_CTF || cgs.gametype == GT_1FCTF) { + trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONGETFLAG)); + } else { + trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONOFFENSE)); + } + trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_OFFENSE)); +} + +static void CG_TaskDefense_f (void ) { + trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONDEFENSE)); + trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_DEFENSE)); +} + +static void CG_TaskPatrol_f (void ) { + trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONPATROL)); + trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_PATROL)); +} + +static void CG_TaskCamp_f (void ) { + trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONCAMPING)); + trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_CAMP)); +} + +static void CG_TaskFollow_f (void ) { + trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONFOLLOW)); + trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_FOLLOW)); +} + +static void CG_TaskRetrieve_f (void ) { + trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONRETURNFLAG)); + trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_RETRIEVE)); +} + +static void CG_TaskEscort_f (void ) { + trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONFOLLOWCARRIER)); + trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_ESCORT)); +} + +static void CG_TaskOwnFlag_f (void ) { + trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_IHAVEFLAG)); +} + +static void CG_TauntKillInsult_f (void ) { + trap_SendConsoleCommand("cmd vsay kill_insult\n"); +} + +static void CG_TauntPraise_f (void ) { + trap_SendConsoleCommand("cmd vsay praise\n"); +} + +static void CG_TauntTaunt_f (void ) { + trap_SendConsoleCommand("cmd vtaunt\n"); +} + +static void CG_TauntDeathInsult_f (void ) { + trap_SendConsoleCommand("cmd vsay death_insult\n"); +} + +static void CG_TauntGauntlet_f (void ) { + trap_SendConsoleCommand("cmd vsay kill_guantlet\n"); +} + +static void CG_TaskSuicide_f (void ) { + int clientNum; + char command[128]; + + clientNum = CG_CrosshairPlayer(); + if ( clientNum == -1 ) { + return; + } + + Com_sprintf( command, 128, "tell %i suicide", clientNum ); + trap_SendClientCommand( command ); +} + + + +/* +================== +CG_TeamMenu_f +================== +*/ +/* +static void CG_TeamMenu_f( void ) { + if (trap_Key_GetCatcher() & KEYCATCH_CGAME) { + CG_EventHandling(CGAME_EVENT_NONE); + trap_Key_SetCatcher(0); + } else { + CG_EventHandling(CGAME_EVENT_TEAMMENU); + //trap_Key_SetCatcher(KEYCATCH_CGAME); + } +} +*/ + +/* +================== +CG_EditHud_f +================== +*/ +/* +static void CG_EditHud_f( void ) { + //cls.keyCatchers ^= KEYCATCH_CGAME; + //VM_Call (cgvm, CG_EVENT_HANDLING, (cls.keyCatchers & KEYCATCH_CGAME) ? CGAME_EVENT_EDITHUD : CGAME_EVENT_NONE); +} +*/ + +#endif + +/* +================== +CG_StartOrbit_f +================== +*/ + +static void CG_StartOrbit_f( void ) { + char var[MAX_TOKEN_CHARS]; + + trap_Cvar_VariableStringBuffer( "developer", var, sizeof( var ) ); + if ( !atoi(var) ) { + return; + } + if (cg_cameraOrbit.value != 0) { + trap_Cvar_Set ("cg_cameraOrbit", "0"); + trap_Cvar_Set("cg_thirdPerson", "0"); + } else { + trap_Cvar_Set("cg_cameraOrbit", "5"); + trap_Cvar_Set("cg_thirdPerson", "1"); + trap_Cvar_Set("cg_thirdPersonAngle", "0"); + trap_Cvar_Set("cg_thirdPersonRange", "100"); + } +} + +/* +static void CG_Camera_f( void ) { + char name[1024]; + trap_Argv( 1, name, sizeof(name)); + if (trap_loadCamera(name)) { + cg.cameraMode = qtrue; + trap_startCamera(cg.time); + } else { + CG_Printf ("Unable to load camera %s\n",name); + } +} +*/ + + +typedef struct { + char *cmd; + void (*function)(void); +} consoleCommand_t; + +static consoleCommand_t commands[] = { + { "testgun", CG_TestGun_f }, + { "testmodel", CG_TestModel_f }, + { "nextframe", CG_TestModelNextFrame_f }, + { "prevframe", CG_TestModelPrevFrame_f }, + { "nextskin", CG_TestModelNextSkin_f }, + { "prevskin", CG_TestModelPrevSkin_f }, + { "viewpos", CG_Viewpos_f }, + { "+scores", CG_ScoresDown_f }, + { "-scores", CG_ScoresUp_f }, + { "+zoom", CG_ZoomDown_f }, + { "-zoom", CG_ZoomUp_f }, + { "sizeup", CG_SizeUp_f }, + { "sizedown", CG_SizeDown_f }, + { "weapnext", CG_NextWeapon_f }, + { "weapprev", CG_PrevWeapon_f }, + { "weapon", CG_Weapon_f }, + { "tell_target", CG_TellTarget_f }, + { "tell_attacker", CG_TellAttacker_f }, + { "vtell_target", CG_VoiceTellTarget_f }, + { "vtell_attacker", CG_VoiceTellAttacker_f }, + { "tcmd", CG_TargetCommand_f }, +#ifdef MISSIONPACK + { "loadhud", CG_LoadHud_f }, + { "nextTeamMember", CG_NextTeamMember_f }, + { "prevTeamMember", CG_PrevTeamMember_f }, + { "nextOrder", CG_NextOrder_f }, + { "confirmOrder", CG_ConfirmOrder_f }, + { "denyOrder", CG_DenyOrder_f }, + { "taskOffense", CG_TaskOffense_f }, + { "taskDefense", CG_TaskDefense_f }, + { "taskPatrol", CG_TaskPatrol_f }, + { "taskCamp", CG_TaskCamp_f }, + { "taskFollow", CG_TaskFollow_f }, + { "taskRetrieve", CG_TaskRetrieve_f }, + { "taskEscort", CG_TaskEscort_f }, + { "taskSuicide", CG_TaskSuicide_f }, + { "taskOwnFlag", CG_TaskOwnFlag_f }, + { "tauntKillInsult", CG_TauntKillInsult_f }, + { "tauntPraise", CG_TauntPraise_f }, + { "tauntTaunt", CG_TauntTaunt_f }, + { "tauntDeathInsult", CG_TauntDeathInsult_f }, + { "tauntGauntlet", CG_TauntGauntlet_f }, + { "spWin", CG_spWin_f }, + { "spLose", CG_spLose_f }, + { "scoresDown", CG_scrollScoresDown_f }, + { "scoresUp", CG_scrollScoresUp_f }, +#endif + { "startOrbit", CG_StartOrbit_f }, + //{ "camera", CG_Camera_f }, + { "loaddeferred", CG_LoadDeferredPlayers } +}; + + +/* +================= +CG_ConsoleCommand + +The string has been tokenized and can be retrieved with +Cmd_Argc() / Cmd_Argv() +================= +*/ +qboolean CG_ConsoleCommand( void ) { + const char *cmd; + int i; + + cmd = CG_Argv(0); + + for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ ) { + if ( !Q_stricmp( cmd, commands[i].cmd ) ) { + commands[i].function(); + return qtrue; + } + } + + return qfalse; +} + + +/* +================= +CG_InitConsoleCommands + +Let the client system know about all of our commands +so it can perform tab completion +================= +*/ +void CG_InitConsoleCommands( void ) { + int i; + + for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ ) { + trap_AddCommand( commands[i].cmd ); + } + + // + // the game server will interpret these commands, which will be automatically + // forwarded to the server after they are not recognized locally + // + trap_AddCommand ("kill"); + trap_AddCommand ("say"); + trap_AddCommand ("say_team"); + trap_AddCommand ("tell"); + trap_AddCommand ("vsay"); + trap_AddCommand ("vsay_team"); + trap_AddCommand ("vtell"); + trap_AddCommand ("vtaunt"); + trap_AddCommand ("vosay"); + trap_AddCommand ("vosay_team"); + trap_AddCommand ("votell"); + trap_AddCommand ("give"); + trap_AddCommand ("god"); + trap_AddCommand ("notarget"); + trap_AddCommand ("noclip"); + trap_AddCommand ("team"); + trap_AddCommand ("follow"); + trap_AddCommand ("levelshot"); + trap_AddCommand ("addbot"); + trap_AddCommand ("setviewpos"); + trap_AddCommand ("callvote"); + trap_AddCommand ("vote"); + trap_AddCommand ("callteamvote"); + trap_AddCommand ("teamvote"); + trap_AddCommand ("stats"); + trap_AddCommand ("teamtask"); + trap_AddCommand ("loaddefered"); // spelled wrong, but not changing for demo +} diff --git a/reaction/engine/code/cgame/cg_draw.c b/reaction/engine/code/cgame/cg_draw.c new file mode 100644 index 00000000..554f4f16 --- /dev/null +++ b/reaction/engine/code/cgame/cg_draw.c @@ -0,0 +1,2663 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_draw.c -- draw all of the graphical elements during +// active (after loading) gameplay + +#include "cg_local.h" + +#ifdef MISSIONPACK +#include "../ui/ui_shared.h" + +// used for scoreboard +extern displayContextDef_t cgDC; +menuDef_t *menuScoreboard = NULL; +#else +int drawTeamOverlayModificationCount = -1; +#endif + +int sortedTeamPlayers[TEAM_MAXOVERLAY]; +int numSortedTeamPlayers; + +char systemChat[256]; +char teamChat1[256]; +char teamChat2[256]; + +#ifdef MISSIONPACK + +int CG_Text_Width(const char *text, float scale, int limit) { + int count,len; + float out; + glyphInfo_t *glyph; + float useScale; +// FIXME: see ui_main.c, same problem +// const unsigned char *s = text; + const char *s = text; + fontInfo_t *font = &cgDC.Assets.textFont; + if (scale <= cg_smallFont.value) { + font = &cgDC.Assets.smallFont; + } else if (scale > cg_bigFont.value) { + font = &cgDC.Assets.bigFont; + } + useScale = scale * font->glyphScale; + out = 0; + if (text) { + len = strlen(text); + if (limit > 0 && len > limit) { + len = limit; + } + count = 0; + while (s && *s && count < len) { + if ( Q_IsColorString(s) ) { + s += 2; + continue; + } else { + glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build + out += glyph->xSkip; + s++; + count++; + } + } + } + return out * useScale; +} + +int CG_Text_Height(const char *text, float scale, int limit) { + int len, count; + float max; + glyphInfo_t *glyph; + float useScale; +// TTimo: FIXME +// const unsigned char *s = text; + const char *s = text; + fontInfo_t *font = &cgDC.Assets.textFont; + if (scale <= cg_smallFont.value) { + font = &cgDC.Assets.smallFont; + } else if (scale > cg_bigFont.value) { + font = &cgDC.Assets.bigFont; + } + useScale = scale * font->glyphScale; + max = 0; + if (text) { + len = strlen(text); + if (limit > 0 && len > limit) { + len = limit; + } + count = 0; + while (s && *s && count < len) { + if ( Q_IsColorString(s) ) { + s += 2; + continue; + } else { + glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build + if (max < glyph->height) { + max = glyph->height; + } + s++; + count++; + } + } + } + return max * useScale; +} + +void CG_Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader) { + float w, h; + w = width * scale; + h = height * scale; + CG_AdjustFrom640( &x, &y, &w, &h ); + trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader ); +} + +void CG_Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style) { + int len, count; + vec4_t newColor; + glyphInfo_t *glyph; + float useScale; + fontInfo_t *font = &cgDC.Assets.textFont; + if (scale <= cg_smallFont.value) { + font = &cgDC.Assets.smallFont; + } else if (scale > cg_bigFont.value) { + font = &cgDC.Assets.bigFont; + } + useScale = scale * font->glyphScale; + if (text) { +// TTimo: FIXME +// const unsigned char *s = text; + const char *s = text; + trap_R_SetColor( color ); + memcpy(&newColor[0], &color[0], sizeof(vec4_t)); + len = strlen(text); + if (limit > 0 && len > limit) { + len = limit; + } + count = 0; + while (s && *s && count < len) { + glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build + //int yadj = Assets.textFont.glyphs[text[i]].bottom + Assets.textFont.glyphs[text[i]].top; + //float yadj = scale * (Assets.textFont.glyphs[text[i]].imageHeight - Assets.textFont.glyphs[text[i]].height); + if ( Q_IsColorString( s ) ) { + memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) ); + newColor[3] = color[3]; + trap_R_SetColor( newColor ); + s += 2; + continue; + } else { + float yadj = useScale * glyph->top; + if (style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE) { + int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2; + colorBlack[3] = newColor[3]; + trap_R_SetColor( colorBlack ); + CG_Text_PaintChar(x + ofs, y - yadj + ofs, + glyph->imageWidth, + glyph->imageHeight, + useScale, + glyph->s, + glyph->t, + glyph->s2, + glyph->t2, + glyph->glyph); + colorBlack[3] = 1.0; + trap_R_SetColor( newColor ); + } + CG_Text_PaintChar(x, y - yadj, + glyph->imageWidth, + glyph->imageHeight, + useScale, + glyph->s, + glyph->t, + glyph->s2, + glyph->t2, + glyph->glyph); + // CG_DrawPic(x, y - yadj, scale * cgDC.Assets.textFont.glyphs[text[i]].imageWidth, scale * cgDC.Assets.textFont.glyphs[text[i]].imageHeight, cgDC.Assets.textFont.glyphs[text[i]].glyph); + x += (glyph->xSkip * useScale) + adjust; + s++; + count++; + } + } + trap_R_SetColor( NULL ); + } +} + + +#endif + +/* +============== +CG_DrawField + +Draws large numbers for status bar and powerups +============== +*/ +#ifndef MISSIONPACK +static void CG_DrawField (int x, int y, int width, int value) { + char num[16], *ptr; + int l; + int frame; + + if ( width < 1 ) { + return; + } + + // draw number string + if ( width > 5 ) { + width = 5; + } + + switch ( width ) { + case 1: + value = value > 9 ? 9 : value; + value = value < 0 ? 0 : value; + break; + case 2: + value = value > 99 ? 99 : value; + value = value < -9 ? -9 : value; + break; + case 3: + value = value > 999 ? 999 : value; + value = value < -99 ? -99 : value; + break; + case 4: + value = value > 9999 ? 9999 : value; + value = value < -999 ? -999 : value; + break; + } + + Com_sprintf (num, sizeof(num), "%i", value); + l = strlen(num); + if (l > width) + l = width; + x += 2 + CHAR_WIDTH*(width - l); + + ptr = num; + while (*ptr && l) + { + if (*ptr == '-') + frame = STAT_MINUS; + else + frame = *ptr -'0'; + + CG_DrawPic( x,y, CHAR_WIDTH, CHAR_HEIGHT, cgs.media.numberShaders[frame] ); + x += CHAR_WIDTH; + ptr++; + l--; + } +} +#endif // MISSIONPACK + +/* +================ +CG_Draw3DModel + +================ +*/ +void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles ) { + refdef_t refdef; + refEntity_t ent; + + if ( !cg_draw3dIcons.integer || !cg_drawIcons.integer ) { + return; + } + + CG_AdjustFrom640( &x, &y, &w, &h ); + + memset( &refdef, 0, sizeof( refdef ) ); + + memset( &ent, 0, sizeof( ent ) ); + AnglesToAxis( angles, ent.axis ); + VectorCopy( origin, ent.origin ); + ent.hModel = model; + ent.customSkin = skin; + ent.renderfx = RF_NOSHADOW; // no stencil shadows + + refdef.rdflags = RDF_NOWORLDMODEL; + + AxisClear( refdef.viewaxis ); + + refdef.fov_x = 30; + refdef.fov_y = 30; + + refdef.x = x; + refdef.y = y; + refdef.width = w; + refdef.height = h; + + refdef.time = cg.time; + + trap_R_ClearScene(); + trap_R_AddRefEntityToScene( &ent ); + trap_R_RenderScene( &refdef ); +} + +/* +================ +CG_DrawHead + +Used for both the status bar and the scoreboard +================ +*/ +void CG_DrawHead( float x, float y, float w, float h, int clientNum, vec3_t headAngles ) { + clipHandle_t cm; + clientInfo_t *ci; + float len; + vec3_t origin; + vec3_t mins, maxs; + + ci = &cgs.clientinfo[ clientNum ]; + + if ( cg_draw3dIcons.integer ) { + cm = ci->headModel; + if ( !cm ) { + return; + } + + // offset the origin y and z to center the head + trap_R_ModelBounds( cm, mins, maxs ); + + origin[2] = -0.5 * ( mins[2] + maxs[2] ); + origin[1] = 0.5 * ( mins[1] + maxs[1] ); + + // calculate distance so the head nearly fills the box + // assume heads are taller than wide + len = 0.7 * ( maxs[2] - mins[2] ); + origin[0] = len / 0.268; // len / tan( fov/2 ) + + // allow per-model tweaking + VectorAdd( origin, ci->headOffset, origin ); + + CG_Draw3DModel( x, y, w, h, ci->headModel, ci->headSkin, origin, headAngles ); + } else if ( cg_drawIcons.integer ) { + CG_DrawPic( x, y, w, h, ci->modelIcon ); + } + + // if they are deferred, draw a cross out + if ( ci->deferred ) { + CG_DrawPic( x, y, w, h, cgs.media.deferShader ); + } +} + +/* +================ +CG_DrawFlagModel + +Used for both the status bar and the scoreboard +================ +*/ +void CG_DrawFlagModel( float x, float y, float w, float h, int team, qboolean force2D ) { + qhandle_t cm; + float len; + vec3_t origin, angles; + vec3_t mins, maxs; + qhandle_t handle; + + if ( !force2D && cg_draw3dIcons.integer ) { + + VectorClear( angles ); + + cm = cgs.media.redFlagModel; + + // offset the origin y and z to center the flag + trap_R_ModelBounds( cm, mins, maxs ); + + origin[2] = -0.5 * ( mins[2] + maxs[2] ); + origin[1] = 0.5 * ( mins[1] + maxs[1] ); + + // calculate distance so the flag nearly fills the box + // assume heads are taller than wide + len = 0.5 * ( maxs[2] - mins[2] ); + origin[0] = len / 0.268; // len / tan( fov/2 ) + + angles[YAW] = 60 * sin( cg.time / 2000.0 );; + + if( team == TEAM_RED ) { + handle = cgs.media.redFlagModel; + } else if( team == TEAM_BLUE ) { + handle = cgs.media.blueFlagModel; + } else if( team == TEAM_FREE ) { + handle = cgs.media.neutralFlagModel; + } else { + return; + } + CG_Draw3DModel( x, y, w, h, handle, 0, origin, angles ); + } else if ( cg_drawIcons.integer ) { + gitem_t *item; + + if( team == TEAM_RED ) { + item = BG_FindItemForPowerup( PW_REDFLAG ); + } else if( team == TEAM_BLUE ) { + item = BG_FindItemForPowerup( PW_BLUEFLAG ); + } else if( team == TEAM_FREE ) { + item = BG_FindItemForPowerup( PW_NEUTRALFLAG ); + } else { + return; + } + if (item) { + CG_DrawPic( x, y, w, h, cg_items[ ITEM_INDEX(item) ].icon ); + } + } +} + +/* +================ +CG_DrawStatusBarHead + +================ +*/ +#ifndef MISSIONPACK + +static void CG_DrawStatusBarHead( float x ) { + vec3_t angles; + float size, stretch; + float frac; + + VectorClear( angles ); + + if ( cg.damageTime && cg.time - cg.damageTime < DAMAGE_TIME ) { + frac = (float)(cg.time - cg.damageTime ) / DAMAGE_TIME; + size = ICON_SIZE * 1.25 * ( 1.5 - frac * 0.5 ); + + stretch = size - ICON_SIZE * 1.25; + // kick in the direction of damage + x -= stretch * 0.5 + cg.damageX * stretch * 0.5; + + cg.headStartYaw = 180 + cg.damageX * 45; + + cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI ); + cg.headEndPitch = 5 * cos( crandom()*M_PI ); + + cg.headStartTime = cg.time; + cg.headEndTime = cg.time + 100 + random() * 2000; + } else { + if ( cg.time >= cg.headEndTime ) { + // select a new head angle + cg.headStartYaw = cg.headEndYaw; + cg.headStartPitch = cg.headEndPitch; + cg.headStartTime = cg.headEndTime; + cg.headEndTime = cg.time + 100 + random() * 2000; + + cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI ); + cg.headEndPitch = 5 * cos( crandom()*M_PI ); + } + + size = ICON_SIZE * 1.25; + } + + // if the server was frozen for a while we may have a bad head start time + if ( cg.headStartTime > cg.time ) { + cg.headStartTime = cg.time; + } + + frac = ( cg.time - cg.headStartTime ) / (float)( cg.headEndTime - cg.headStartTime ); + frac = frac * frac * ( 3 - 2 * frac ); + angles[YAW] = cg.headStartYaw + ( cg.headEndYaw - cg.headStartYaw ) * frac; + angles[PITCH] = cg.headStartPitch + ( cg.headEndPitch - cg.headStartPitch ) * frac; + + CG_DrawHead( x, 480 - size, size, size, + cg.snap->ps.clientNum, angles ); +} +#endif // MISSIONPACK + +/* +================ +CG_DrawStatusBarFlag + +================ +*/ +#ifndef MISSIONPACK +static void CG_DrawStatusBarFlag( float x, int team ) { + CG_DrawFlagModel( x, 480 - ICON_SIZE, ICON_SIZE, ICON_SIZE, team, qfalse ); +} +#endif // MISSIONPACK + +/* +================ +CG_DrawTeamBackground + +================ +*/ +void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team ) +{ + vec4_t hcolor; + + hcolor[3] = alpha; + if ( team == TEAM_RED ) { + hcolor[0] = 1; + hcolor[1] = 0; + hcolor[2] = 0; + } else if ( team == TEAM_BLUE ) { + hcolor[0] = 0; + hcolor[1] = 0; + hcolor[2] = 1; + } else { + return; + } + trap_R_SetColor( hcolor ); + CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar ); + trap_R_SetColor( NULL ); +} + +/* +================ +CG_DrawStatusBar + +================ +*/ +#ifndef MISSIONPACK +static void CG_DrawStatusBar( void ) { + int color; + centity_t *cent; + playerState_t *ps; + int value; + vec4_t hcolor; + vec3_t angles; + vec3_t origin; + + static float colors[4][4] = { +// { 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 }, {0.5, 0.5, 0.5, 1} }; + { 1.0f, 0.69f, 0.0f, 1.0f }, // normal + { 1.0f, 0.2f, 0.2f, 1.0f }, // low health + { 0.5f, 0.5f, 0.5f, 1.0f }, // weapon firing + { 1.0f, 1.0f, 1.0f, 1.0f } }; // health > 100 + + if ( cg_drawStatus.integer == 0 ) { + return; + } + + // draw the team background + CG_DrawTeamBackground( 0, 420, 640, 60, 0.33f, cg.snap->ps.persistant[PERS_TEAM] ); + + cent = &cg_entities[cg.snap->ps.clientNum]; + ps = &cg.snap->ps; + + VectorClear( angles ); + + // draw any 3D icons first, so the changes back to 2D are minimized + if ( cent->currentState.weapon && cg_weapons[ cent->currentState.weapon ].ammoModel ) { + origin[0] = 70; + origin[1] = 0; + origin[2] = 0; + angles[YAW] = 90 + 20 * sin( cg.time / 1000.0 ); + CG_Draw3DModel( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, + cg_weapons[ cent->currentState.weapon ].ammoModel, 0, origin, angles ); + } + + CG_DrawStatusBarHead( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE ); + + if( cg.predictedPlayerState.powerups[PW_REDFLAG] ) { + CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_RED ); + } else if( cg.predictedPlayerState.powerups[PW_BLUEFLAG] ) { + CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_BLUE ); + } else if( cg.predictedPlayerState.powerups[PW_NEUTRALFLAG] ) { + CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_FREE ); + } + + if ( ps->stats[ STAT_ARMOR ] ) { + origin[0] = 90; + origin[1] = 0; + origin[2] = -10; + angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0; + CG_Draw3DModel( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, + cgs.media.armorModel, 0, origin, angles ); + } + // + // ammo + // + if ( cent->currentState.weapon ) { + value = ps->ammo[cent->currentState.weapon]; + if ( value > -1 ) { + if ( cg.predictedPlayerState.weaponstate == WEAPON_FIRING + && cg.predictedPlayerState.weaponTime > 100 ) { + // draw as dark grey when reloading + color = 2; // dark grey + } else { + if ( value >= 0 ) { + color = 0; // green + } else { + color = 1; // red + } + } + trap_R_SetColor( colors[color] ); + + CG_DrawField (0, 432, 3, value); + trap_R_SetColor( NULL ); + + // if we didn't draw a 3D icon, draw a 2D icon for ammo + if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) { + qhandle_t icon; + + icon = cg_weapons[ cg.predictedPlayerState.weapon ].ammoIcon; + if ( icon ) { + CG_DrawPic( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, icon ); + } + } + } + } + + // + // health + // + value = ps->stats[STAT_HEALTH]; + if ( value > 100 ) { + trap_R_SetColor( colors[3] ); // white + } else if (value > 25) { + trap_R_SetColor( colors[0] ); // green + } else if (value > 0) { + color = (cg.time >> 8) & 1; // flash + trap_R_SetColor( colors[color] ); + } else { + trap_R_SetColor( colors[1] ); // red + } + + // stretch the health up when taking damage + CG_DrawField ( 185, 432, 3, value); + CG_ColorForHealth( hcolor ); + trap_R_SetColor( hcolor ); + + + // + // armor + // + value = ps->stats[STAT_ARMOR]; + if (value > 0 ) { + trap_R_SetColor( colors[0] ); + CG_DrawField (370, 432, 3, value); + trap_R_SetColor( NULL ); + // if we didn't draw a 3D icon, draw a 2D icon for armor + if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) { + CG_DrawPic( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, cgs.media.armorIcon ); + } + + } +} +#endif + +/* +=========================================================================================== + + UPPER RIGHT CORNER + +=========================================================================================== +*/ + +/* +================ +CG_DrawAttacker + +================ +*/ +static float CG_DrawAttacker( float y ) { + int t; + float size; + vec3_t angles; + const char *info; + const char *name; + int clientNum; + + if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { + return y; + } + + if ( !cg.attackerTime ) { + return y; + } + + clientNum = cg.predictedPlayerState.persistant[PERS_ATTACKER]; + if ( clientNum < 0 || clientNum >= MAX_CLIENTS || clientNum == cg.snap->ps.clientNum ) { + return y; + } + + t = cg.time - cg.attackerTime; + if ( t > ATTACKER_HEAD_TIME ) { + cg.attackerTime = 0; + return y; + } + + size = ICON_SIZE * 1.25; + + angles[PITCH] = 0; + angles[YAW] = 180; + angles[ROLL] = 0; + CG_DrawHead( 640 - size, y, size, size, clientNum, angles ); + + info = CG_ConfigString( CS_PLAYERS + clientNum ); + name = Info_ValueForKey( info, "n" ); + y += size; + CG_DrawBigString( 640 - ( Q_PrintStrlen( name ) * BIGCHAR_WIDTH), y, name, 0.5 ); + + return y + BIGCHAR_HEIGHT + 2; +} + +/* +================== +CG_DrawSnapshot +================== +*/ +static float CG_DrawSnapshot( float y ) { + char *s; + int w; + + s = va( "time:%i snap:%i cmd:%i", cg.snap->serverTime, + cg.latestSnapshotNum, cgs.serverCommandSequence ); + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; + + CG_DrawBigString( 635 - w, y + 2, s, 1.0F); + + return y + BIGCHAR_HEIGHT + 4; +} + +/* +================== +CG_DrawFPS +================== +*/ +#define FPS_FRAMES 4 +static float CG_DrawFPS( float y ) { + char *s; + int w; + static int previousTimes[FPS_FRAMES]; + static int index; + int i, total; + int fps; + static int previous; + int t, frameTime; + + // don't use serverTime, because that will be drifting to + // correct for internet lag changes, timescales, timedemos, etc + t = trap_Milliseconds(); + frameTime = t - previous; + previous = t; + + previousTimes[index % FPS_FRAMES] = frameTime; + index++; + if ( index > FPS_FRAMES ) { + // average multiple frames together to smooth changes out a bit + total = 0; + for ( i = 0 ; i < FPS_FRAMES ; i++ ) { + total += previousTimes[i]; + } + if ( !total ) { + total = 1; + } + fps = 1000 * FPS_FRAMES / total; + + s = va( "%ifps", fps ); + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; + + CG_DrawBigString( 635 - w, y + 2, s, 1.0F); + } + + return y + BIGCHAR_HEIGHT + 4; +} + +/* +================= +CG_DrawTimer +================= +*/ +static float CG_DrawTimer( float y ) { + char *s; + int w; + int mins, seconds, tens; + int msec; + + msec = cg.time - cgs.levelStartTime; + + seconds = msec / 1000; + mins = seconds / 60; + seconds -= mins * 60; + tens = seconds / 10; + seconds -= tens * 10; + + s = va( "%i:%i%i", mins, tens, seconds ); + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; + + CG_DrawBigString( 635 - w, y + 2, s, 1.0F); + + return y + BIGCHAR_HEIGHT + 4; +} + + +/* +================= +CG_DrawTeamOverlay +================= +*/ + +static float CG_DrawTeamOverlay( float y, qboolean right, qboolean upper ) { + int x, w, h, xx; + int i, j, len; + const char *p; + vec4_t hcolor; + int pwidth, lwidth; + int plyrs; + char st[16]; + clientInfo_t *ci; + gitem_t *item; + int ret_y, count; + + if ( !cg_drawTeamOverlay.integer ) { + return y; + } + + if ( cg.snap->ps.persistant[PERS_TEAM] != TEAM_RED && cg.snap->ps.persistant[PERS_TEAM] != TEAM_BLUE ) { + return y; // Not on any team + } + + plyrs = 0; + + // max player name width + pwidth = 0; + count = (numSortedTeamPlayers > 8) ? 8 : numSortedTeamPlayers; + for (i = 0; i < count; i++) { + ci = cgs.clientinfo + sortedTeamPlayers[i]; + if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) { + plyrs++; + len = CG_DrawStrlen(ci->name); + if (len > pwidth) + pwidth = len; + } + } + + if (!plyrs) + return y; + + if (pwidth > TEAM_OVERLAY_MAXNAME_WIDTH) + pwidth = TEAM_OVERLAY_MAXNAME_WIDTH; + + // max location name width + lwidth = 0; + for (i = 1; i < MAX_LOCATIONS; i++) { + p = CG_ConfigString(CS_LOCATIONS + i); + if (p && *p) { + len = CG_DrawStrlen(p); + if (len > lwidth) + lwidth = len; + } + } + + if (lwidth > TEAM_OVERLAY_MAXLOCATION_WIDTH) + lwidth = TEAM_OVERLAY_MAXLOCATION_WIDTH; + + w = (pwidth + lwidth + 4 + 7) * TINYCHAR_WIDTH; + + if ( right ) + x = 640 - w; + else + x = 0; + + h = plyrs * TINYCHAR_HEIGHT; + + if ( upper ) { + ret_y = y + h; + } else { + y -= h; + ret_y = y; + } + + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) { + hcolor[0] = 1.0f; + hcolor[1] = 0.0f; + hcolor[2] = 0.0f; + hcolor[3] = 0.33f; + } else { // if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) + hcolor[0] = 0.0f; + hcolor[1] = 0.0f; + hcolor[2] = 1.0f; + hcolor[3] = 0.33f; + } + trap_R_SetColor( hcolor ); + CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar ); + trap_R_SetColor( NULL ); + + for (i = 0; i < count; i++) { + ci = cgs.clientinfo + sortedTeamPlayers[i]; + if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) { + + hcolor[0] = hcolor[1] = hcolor[2] = hcolor[3] = 1.0; + + xx = x + TINYCHAR_WIDTH; + + CG_DrawStringExt( xx, y, + ci->name, hcolor, qfalse, qfalse, + TINYCHAR_WIDTH, TINYCHAR_HEIGHT, TEAM_OVERLAY_MAXNAME_WIDTH); + + if (lwidth) { + p = CG_ConfigString(CS_LOCATIONS + ci->location); + if (!p || !*p) + p = "unknown"; + len = CG_DrawStrlen(p); + if (len > lwidth) + len = lwidth; + +// xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth + +// ((lwidth/2 - len/2) * TINYCHAR_WIDTH); + xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth; + CG_DrawStringExt( xx, y, + p, hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, + TEAM_OVERLAY_MAXLOCATION_WIDTH); + } + + CG_GetColorForHealth( ci->health, ci->armor, hcolor ); + + Com_sprintf (st, sizeof(st), "%3i %3i", ci->health, ci->armor); + + xx = x + TINYCHAR_WIDTH * 3 + + TINYCHAR_WIDTH * pwidth + TINYCHAR_WIDTH * lwidth; + + CG_DrawStringExt( xx, y, + st, hcolor, qfalse, qfalse, + TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 ); + + // draw weapon icon + xx += TINYCHAR_WIDTH * 3; + + if ( cg_weapons[ci->curWeapon].weaponIcon ) { + CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, + cg_weapons[ci->curWeapon].weaponIcon ); + } else { + CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, + cgs.media.deferShader ); + } + + // Draw powerup icons + if (right) { + xx = x; + } else { + xx = x + w - TINYCHAR_WIDTH; + } + for (j = 0; j <= PW_NUM_POWERUPS; j++) { + if (ci->powerups & (1 << j)) { + + item = BG_FindItemForPowerup( j ); + + if (item) { + CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, + trap_R_RegisterShader( item->icon ) ); + if (right) { + xx -= TINYCHAR_WIDTH; + } else { + xx += TINYCHAR_WIDTH; + } + } + } + } + + y += TINYCHAR_HEIGHT; + } + } + + return ret_y; +//#endif +} + + +/* +===================== +CG_DrawUpperRight + +===================== +*/ +static void CG_DrawUpperRight(stereoFrame_t stereoFrame) +{ + float y; + + y = 0; + + if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 1 ) { + y = CG_DrawTeamOverlay( y, qtrue, qtrue ); + } + if ( cg_drawSnapshot.integer ) { + y = CG_DrawSnapshot( y ); + } + if (cg_drawFPS.integer && (stereoFrame == STEREO_CENTER || stereoFrame == STEREO_RIGHT)) { + y = CG_DrawFPS( y ); + } + if ( cg_drawTimer.integer ) { + y = CG_DrawTimer( y ); + } + if ( cg_drawAttacker.integer ) { + y = CG_DrawAttacker( y ); + } + +} + +/* +=========================================================================================== + + LOWER RIGHT CORNER + +=========================================================================================== +*/ + +/* +================= +CG_DrawScores + +Draw the small two score display +================= +*/ +#ifndef MISSIONPACK +static float CG_DrawScores( float y ) { + const char *s; + int s1, s2, score; + int x, w; + int v; + vec4_t color; + float y1; + gitem_t *item; + + s1 = cgs.scores1; + s2 = cgs.scores2; + + y -= BIGCHAR_HEIGHT + 8; + + y1 = y; + + // draw from the right side to left + if ( cgs.gametype >= GT_TEAM ) { + x = 640; + color[0] = 0.0f; + color[1] = 0.0f; + color[2] = 1.0f; + color[3] = 0.33f; + s = va( "%2i", s2 ); + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; + x -= w; + CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) { + CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader ); + } + CG_DrawBigString( x + 4, y, s, 1.0F); + + if ( cgs.gametype == GT_CTF ) { + // Display flag status + item = BG_FindItemForPowerup( PW_BLUEFLAG ); + + if (item) { + y1 = y - BIGCHAR_HEIGHT - 8; + if( cgs.blueflag >= 0 && cgs.blueflag <= 2 ) { + CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.blueFlagShader[cgs.blueflag] ); + } + } + } + color[0] = 1.0f; + color[1] = 0.0f; + color[2] = 0.0f; + color[3] = 0.33f; + s = va( "%2i", s1 ); + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; + x -= w; + CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) { + CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader ); + } + CG_DrawBigString( x + 4, y, s, 1.0F); + + if ( cgs.gametype == GT_CTF ) { + // Display flag status + item = BG_FindItemForPowerup( PW_REDFLAG ); + + if (item) { + y1 = y - BIGCHAR_HEIGHT - 8; + if( cgs.redflag >= 0 && cgs.redflag <= 2 ) { + CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.redFlagShader[cgs.redflag] ); + } + } + } + + if ( cgs.gametype >= GT_CTF ) { + v = cgs.capturelimit; + } else { + v = cgs.fraglimit; + } + if ( v ) { + s = va( "%2i", v ); + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; + x -= w; + CG_DrawBigString( x + 4, y, s, 1.0F); + } + + } else { + qboolean spectator; + + x = 640; + score = cg.snap->ps.persistant[PERS_SCORE]; + spectator = ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ); + + // always show your score in the second box if not in first place + if ( s1 != score ) { + s2 = score; + } + if ( s2 != SCORE_NOT_PRESENT ) { + s = va( "%2i", s2 ); + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; + x -= w; + if ( !spectator && score == s2 && score != s1 ) { + color[0] = 1.0f; + color[1] = 0.0f; + color[2] = 0.0f; + color[3] = 0.33f; + CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); + CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader ); + } else { + color[0] = 0.5f; + color[1] = 0.5f; + color[2] = 0.5f; + color[3] = 0.33f; + CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); + } + CG_DrawBigString( x + 4, y, s, 1.0F); + } + + // first place + if ( s1 != SCORE_NOT_PRESENT ) { + s = va( "%2i", s1 ); + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; + x -= w; + if ( !spectator && score == s1 ) { + color[0] = 0.0f; + color[1] = 0.0f; + color[2] = 1.0f; + color[3] = 0.33f; + CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); + CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader ); + } else { + color[0] = 0.5f; + color[1] = 0.5f; + color[2] = 0.5f; + color[3] = 0.33f; + CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); + } + CG_DrawBigString( x + 4, y, s, 1.0F); + } + + if ( cgs.fraglimit ) { + s = va( "%2i", cgs.fraglimit ); + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; + x -= w; + CG_DrawBigString( x + 4, y, s, 1.0F); + } + + } + + return y1 - 8; +} +#endif // MISSIONPACK + +/* +================ +CG_DrawPowerups +================ +*/ +#ifndef MISSIONPACK +static float CG_DrawPowerups( float y ) { + int sorted[MAX_POWERUPS]; + int sortedTime[MAX_POWERUPS]; + int i, j, k; + int active; + playerState_t *ps; + int t; + gitem_t *item; + int x; + int color; + float size; + float f; + static float colors[2][4] = { + { 0.2f, 1.0f, 0.2f, 1.0f } , + { 1.0f, 0.2f, 0.2f, 1.0f } + }; + + ps = &cg.snap->ps; + + if ( ps->stats[STAT_HEALTH] <= 0 ) { + return y; + } + + // sort the list by time remaining + active = 0; + for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { + if ( !ps->powerups[ i ] ) { + continue; + } + t = ps->powerups[ i ] - cg.time; + // ZOID--don't draw if the power up has unlimited time (999 seconds) + // This is true of the CTF flags + if ( t < 0 || t > 999000) { + continue; + } + + // insert into the list + for ( j = 0 ; j < active ; j++ ) { + if ( sortedTime[j] >= t ) { + for ( k = active - 1 ; k >= j ; k-- ) { + sorted[k+1] = sorted[k]; + sortedTime[k+1] = sortedTime[k]; + } + break; + } + } + sorted[j] = i; + sortedTime[j] = t; + active++; + } + + // draw the icons and timers + x = 640 - ICON_SIZE - CHAR_WIDTH * 2; + for ( i = 0 ; i < active ; i++ ) { + item = BG_FindItemForPowerup( sorted[i] ); + + if (item) { + + color = 1; + + y -= ICON_SIZE; + + trap_R_SetColor( colors[color] ); + CG_DrawField( x, y, 2, sortedTime[ i ] / 1000 ); + + t = ps->powerups[ sorted[i] ]; + if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) { + trap_R_SetColor( NULL ); + } else { + vec4_t modulate; + + f = (float)( t - cg.time ) / POWERUP_BLINK_TIME; + f -= (int)f; + modulate[0] = modulate[1] = modulate[2] = modulate[3] = f; + trap_R_SetColor( modulate ); + } + + if ( cg.powerupActive == sorted[i] && + cg.time - cg.powerupTime < PULSE_TIME ) { + f = 1.0 - ( ( (float)cg.time - cg.powerupTime ) / PULSE_TIME ); + size = ICON_SIZE * ( 1.0 + ( PULSE_SCALE - 1.0 ) * f ); + } else { + size = ICON_SIZE; + } + + CG_DrawPic( 640 - size, y + ICON_SIZE / 2 - size / 2, + size, size, trap_R_RegisterShader( item->icon ) ); + } + } + trap_R_SetColor( NULL ); + + return y; +} +#endif // MISSIONPACK + +/* +===================== +CG_DrawLowerRight + +===================== +*/ +#ifndef MISSIONPACK +static void CG_DrawLowerRight( void ) { + float y; + + y = 480 - ICON_SIZE; + + if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 2 ) { + y = CG_DrawTeamOverlay( y, qtrue, qfalse ); + } + + y = CG_DrawScores( y ); + y = CG_DrawPowerups( y ); +} +#endif // MISSIONPACK + +/* +=================== +CG_DrawPickupItem +=================== +*/ +#ifndef MISSIONPACK +static int CG_DrawPickupItem( int y ) { + int value; + float *fadeColor; + + if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) { + return y; + } + + y -= ICON_SIZE; + + value = cg.itemPickup; + if ( value ) { + fadeColor = CG_FadeColor( cg.itemPickupTime, 3000 ); + if ( fadeColor ) { + CG_RegisterItemVisuals( value ); + trap_R_SetColor( fadeColor ); + CG_DrawPic( 8, y, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon ); + CG_DrawBigString( ICON_SIZE + 16, y + (ICON_SIZE/2 - BIGCHAR_HEIGHT/2), bg_itemlist[ value ].pickup_name, fadeColor[0] ); + trap_R_SetColor( NULL ); + } + } + + return y; +} +#endif // MISSIONPACK + +/* +===================== +CG_DrawLowerLeft + +===================== +*/ +#ifndef MISSIONPACK +static void CG_DrawLowerLeft( void ) { + float y; + + y = 480 - ICON_SIZE; + + if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 3 ) { + y = CG_DrawTeamOverlay( y, qfalse, qfalse ); + } + + + y = CG_DrawPickupItem( y ); +} +#endif // MISSIONPACK + + +//=========================================================================================== + +/* +================= +CG_DrawTeamInfo +================= +*/ +#ifndef MISSIONPACK +static void CG_DrawTeamInfo( void ) { + int w, h; + int i, len; + vec4_t hcolor; + int chatHeight; + +#define CHATLOC_Y 420 // bottom end +#define CHATLOC_X 0 + + if (cg_teamChatHeight.integer < TEAMCHAT_HEIGHT) + chatHeight = cg_teamChatHeight.integer; + else + chatHeight = TEAMCHAT_HEIGHT; + if (chatHeight <= 0) + return; // disabled + + if (cgs.teamLastChatPos != cgs.teamChatPos) { + if (cg.time - cgs.teamChatMsgTimes[cgs.teamLastChatPos % chatHeight] > cg_teamChatTime.integer) { + cgs.teamLastChatPos++; + } + + h = (cgs.teamChatPos - cgs.teamLastChatPos) * TINYCHAR_HEIGHT; + + w = 0; + + for (i = cgs.teamLastChatPos; i < cgs.teamChatPos; i++) { + len = CG_DrawStrlen(cgs.teamChatMsgs[i % chatHeight]); + if (len > w) + w = len; + } + w *= TINYCHAR_WIDTH; + w += TINYCHAR_WIDTH * 2; + + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) { + hcolor[0] = 1.0f; + hcolor[1] = 0.0f; + hcolor[2] = 0.0f; + hcolor[3] = 0.33f; + } else if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) { + hcolor[0] = 0.0f; + hcolor[1] = 0.0f; + hcolor[2] = 1.0f; + hcolor[3] = 0.33f; + } else { + hcolor[0] = 0.0f; + hcolor[1] = 1.0f; + hcolor[2] = 0.0f; + hcolor[3] = 0.33f; + } + + trap_R_SetColor( hcolor ); + CG_DrawPic( CHATLOC_X, CHATLOC_Y - h, 640, h, cgs.media.teamStatusBar ); + trap_R_SetColor( NULL ); + + hcolor[0] = hcolor[1] = hcolor[2] = 1.0f; + hcolor[3] = 1.0f; + + for (i = cgs.teamChatPos - 1; i >= cgs.teamLastChatPos; i--) { + CG_DrawStringExt( CHATLOC_X + TINYCHAR_WIDTH, + CHATLOC_Y - (cgs.teamChatPos - i)*TINYCHAR_HEIGHT, + cgs.teamChatMsgs[i % chatHeight], hcolor, qfalse, qfalse, + TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 ); + } + } +} +#endif // MISSIONPACK + +/* +=================== +CG_DrawHoldableItem +=================== +*/ +#ifndef MISSIONPACK +static void CG_DrawHoldableItem( void ) { + int value; + + value = cg.snap->ps.stats[STAT_HOLDABLE_ITEM]; + if ( value ) { + CG_RegisterItemVisuals( value ); + CG_DrawPic( 640-ICON_SIZE, (SCREEN_HEIGHT-ICON_SIZE)/2, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon ); + } + +} +#endif // MISSIONPACK + +#ifdef MISSIONPACK +/* +=================== +CG_DrawPersistantPowerup +=================== +*/ +#if 0 // sos001208 - DEAD +static void CG_DrawPersistantPowerup( void ) { + int value; + + value = cg.snap->ps.stats[STAT_PERSISTANT_POWERUP]; + if ( value ) { + CG_RegisterItemVisuals( value ); + CG_DrawPic( 640-ICON_SIZE, (SCREEN_HEIGHT-ICON_SIZE)/2 - ICON_SIZE, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon ); + } +} +#endif +#endif // MISSIONPACK + + +/* +=================== +CG_DrawReward +=================== +*/ +static void CG_DrawReward( void ) { + float *color; + int i, count; + float x, y; + char buf[32]; + + if ( !cg_drawRewards.integer ) { + return; + } + + color = CG_FadeColor( cg.rewardTime, REWARD_TIME ); + if ( !color ) { + if (cg.rewardStack > 0) { + for(i = 0; i < cg.rewardStack; i++) { + cg.rewardSound[i] = cg.rewardSound[i+1]; + cg.rewardShader[i] = cg.rewardShader[i+1]; + cg.rewardCount[i] = cg.rewardCount[i+1]; + } + cg.rewardTime = cg.time; + cg.rewardStack--; + color = CG_FadeColor( cg.rewardTime, REWARD_TIME ); + trap_S_StartLocalSound(cg.rewardSound[0], CHAN_ANNOUNCER); + } else { + return; + } + } + + trap_R_SetColor( color ); + + /* + count = cg.rewardCount[0]/10; // number of big rewards to draw + + if (count) { + y = 4; + x = 320 - count * ICON_SIZE; + for ( i = 0 ; i < count ; i++ ) { + CG_DrawPic( x, y, (ICON_SIZE*2)-4, (ICON_SIZE*2)-4, cg.rewardShader[0] ); + x += (ICON_SIZE*2); + } + } + + count = cg.rewardCount[0] - count*10; // number of small rewards to draw + */ + + if ( cg.rewardCount[0] >= 10 ) { + y = 56; + x = 320 - ICON_SIZE/2; + CG_DrawPic( x, y, ICON_SIZE-4, ICON_SIZE-4, cg.rewardShader[0] ); + Com_sprintf(buf, sizeof(buf), "%d", cg.rewardCount[0]); + x = ( SCREEN_WIDTH - SMALLCHAR_WIDTH * CG_DrawStrlen( buf ) ) / 2; + CG_DrawStringExt( x, y+ICON_SIZE, buf, color, qfalse, qtrue, + SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 ); + } + else { + + count = cg.rewardCount[0]; + + y = 56; + x = 320 - count * ICON_SIZE/2; + for ( i = 0 ; i < count ; i++ ) { + CG_DrawPic( x, y, ICON_SIZE-4, ICON_SIZE-4, cg.rewardShader[0] ); + x += ICON_SIZE; + } + } + trap_R_SetColor( NULL ); +} + + +/* +=============================================================================== + +LAGOMETER + +=============================================================================== +*/ + +#define LAG_SAMPLES 128 + + +typedef struct { + int frameSamples[LAG_SAMPLES]; + int frameCount; + int snapshotFlags[LAG_SAMPLES]; + int snapshotSamples[LAG_SAMPLES]; + int snapshotCount; +} lagometer_t; + +lagometer_t lagometer; + +/* +============== +CG_AddLagometerFrameInfo + +Adds the current interpolate / extrapolate bar for this frame +============== +*/ +void CG_AddLagometerFrameInfo( void ) { + int offset; + + offset = cg.time - cg.latestSnapshotTime; + lagometer.frameSamples[ lagometer.frameCount & ( LAG_SAMPLES - 1) ] = offset; + lagometer.frameCount++; +} + +/* +============== +CG_AddLagometerSnapshotInfo + +Each time a snapshot is received, log its ping time and +the number of snapshots that were dropped before it. + +Pass NULL for a dropped packet. +============== +*/ +void CG_AddLagometerSnapshotInfo( snapshot_t *snap ) { + // dropped packet + if ( !snap ) { + lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = -1; + lagometer.snapshotCount++; + return; + } + + // add this snapshot's info + lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->ping; + lagometer.snapshotFlags[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->snapFlags; + lagometer.snapshotCount++; +} + +/* +============== +CG_DrawDisconnect + +Should we draw something differnet for long lag vs no packets? +============== +*/ +static void CG_DrawDisconnect( void ) { + float x, y; + int cmdNum; + usercmd_t cmd; + const char *s; + int w; + + // draw the phone jack if we are completely past our buffers + cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1; + trap_GetUserCmd( cmdNum, &cmd ); + if ( cmd.serverTime <= cg.snap->ps.commandTime + || cmd.serverTime > cg.time ) { // special check for map_restart + return; + } + + // also add text in center of screen + s = "Connection Interrupted"; + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; + CG_DrawBigString( 320 - w/2, 100, s, 1.0F); + + // blink the icon + if ( ( cg.time >> 9 ) & 1 ) { + return; + } + + x = 640 - 48; + y = 480 - 48; + + CG_DrawPic( x, y, 48, 48, trap_R_RegisterShader("gfx/2d/net.tga" ) ); +} + + +#define MAX_LAGOMETER_PING 900 +#define MAX_LAGOMETER_RANGE 300 + +/* +============== +CG_DrawLagometer +============== +*/ +static void CG_DrawLagometer( void ) { + int a, x, y, i; + float v; + float ax, ay, aw, ah, mid, range; + int color; + float vscale; + + if ( !cg_lagometer.integer || cgs.localServer ) { + CG_DrawDisconnect(); + return; + } + + // + // draw the graph + // +#ifdef MISSIONPACK + x = 640 - 48; + y = 480 - 144; +#else + x = 640 - 48; + y = 480 - 48; +#endif + + trap_R_SetColor( NULL ); + CG_DrawPic( x, y, 48, 48, cgs.media.lagometerShader ); + + ax = x; + ay = y; + aw = 48; + ah = 48; + CG_AdjustFrom640( &ax, &ay, &aw, &ah ); + + color = -1; + range = ah / 3; + mid = ay + range; + + vscale = range / MAX_LAGOMETER_RANGE; + + // draw the frame interpoalte / extrapolate graph + for ( a = 0 ; a < aw ; a++ ) { + i = ( lagometer.frameCount - 1 - a ) & (LAG_SAMPLES - 1); + v = lagometer.frameSamples[i]; + v *= vscale; + if ( v > 0 ) { + if ( color != 1 ) { + color = 1; + trap_R_SetColor( g_color_table[ColorIndex(COLOR_YELLOW)] ); + } + if ( v > range ) { + v = range; + } + trap_R_DrawStretchPic ( ax + aw - a, mid - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader ); + } else if ( v < 0 ) { + if ( color != 2 ) { + color = 2; + trap_R_SetColor( g_color_table[ColorIndex(COLOR_BLUE)] ); + } + v = -v; + if ( v > range ) { + v = range; + } + trap_R_DrawStretchPic( ax + aw - a, mid, 1, v, 0, 0, 0, 0, cgs.media.whiteShader ); + } + } + + // draw the snapshot latency / drop graph + range = ah / 2; + vscale = range / MAX_LAGOMETER_PING; + + for ( a = 0 ; a < aw ; a++ ) { + i = ( lagometer.snapshotCount - 1 - a ) & (LAG_SAMPLES - 1); + v = lagometer.snapshotSamples[i]; + if ( v > 0 ) { + if ( lagometer.snapshotFlags[i] & SNAPFLAG_RATE_DELAYED ) { + if ( color != 5 ) { + color = 5; // YELLOW for rate delay + trap_R_SetColor( g_color_table[ColorIndex(COLOR_YELLOW)] ); + } + } else { + if ( color != 3 ) { + color = 3; + trap_R_SetColor( g_color_table[ColorIndex(COLOR_GREEN)] ); + } + } + v = v * vscale; + if ( v > range ) { + v = range; + } + trap_R_DrawStretchPic( ax + aw - a, ay + ah - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader ); + } else if ( v < 0 ) { + if ( color != 4 ) { + color = 4; // RED for dropped snapshots + trap_R_SetColor( g_color_table[ColorIndex(COLOR_RED)] ); + } + trap_R_DrawStretchPic( ax + aw - a, ay + ah - range, 1, range, 0, 0, 0, 0, cgs.media.whiteShader ); + } + } + + trap_R_SetColor( NULL ); + + if ( cg_nopredict.integer || cg_synchronousClients.integer ) { + CG_DrawBigString( ax, ay, "snc", 1.0 ); + } + + CG_DrawDisconnect(); +} + + + +/* +=============================================================================== + +CENTER PRINTING + +=============================================================================== +*/ + + +/* +============== +CG_CenterPrint + +Called for important messages that should stay in the center of the screen +for a few moments +============== +*/ +void CG_CenterPrint( const char *str, int y, int charWidth ) { + char *s; + + Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) ); + + cg.centerPrintTime = cg.time; + cg.centerPrintY = y; + cg.centerPrintCharWidth = charWidth; + + // count the number of lines for centering + cg.centerPrintLines = 1; + s = cg.centerPrint; + while( *s ) { + if (*s == '\n') + cg.centerPrintLines++; + s++; + } +} + + +/* +=================== +CG_DrawCenterString +=================== +*/ +static void CG_DrawCenterString( void ) { + char *start; + int l; + int x, y, w; +#ifdef MISSIONPACK + int h; +#endif + float *color; + + if ( !cg.centerPrintTime ) { + return; + } + + color = CG_FadeColor( cg.centerPrintTime, 1000 * cg_centertime.value ); + if ( !color ) { + return; + } + + trap_R_SetColor( color ); + + start = cg.centerPrint; + + y = cg.centerPrintY - cg.centerPrintLines * BIGCHAR_HEIGHT / 2; + + while ( 1 ) { + char linebuffer[1024]; + + for ( l = 0; l < 50; l++ ) { + if ( !start[l] || start[l] == '\n' ) { + break; + } + linebuffer[l] = start[l]; + } + linebuffer[l] = 0; + +#ifdef MISSIONPACK + w = CG_Text_Width(linebuffer, 0.5, 0); + h = CG_Text_Height(linebuffer, 0.5, 0); + x = (SCREEN_WIDTH - w) / 2; + CG_Text_Paint(x, y + h, 0.5, color, linebuffer, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE); + y += h + 6; +#else + w = cg.centerPrintCharWidth * CG_DrawStrlen( linebuffer ); + + x = ( SCREEN_WIDTH - w ) / 2; + + CG_DrawStringExt( x, y, linebuffer, color, qfalse, qtrue, + cg.centerPrintCharWidth, (int)(cg.centerPrintCharWidth * 1.5), 0 ); + + y += cg.centerPrintCharWidth * 1.5; +#endif + while ( *start && ( *start != '\n' ) ) { + start++; + } + if ( !*start ) { + break; + } + start++; + } + + trap_R_SetColor( NULL ); +} + + + +/* +================================================================================ + +CROSSHAIR + +================================================================================ +*/ + + +/* +================= +CG_DrawCrosshair +================= +*/ +static void CG_DrawCrosshair(void) +{ + float w, h; + qhandle_t hShader; + float f; + float x, y; + int ca; + + if ( !cg_drawCrosshair.integer ) { + return; + } + + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR) { + return; + } + + if ( cg.renderingThirdPerson ) { + return; + } + + // set color based on health + if ( cg_crosshairHealth.integer ) { + vec4_t hcolor; + + CG_ColorForHealth( hcolor ); + trap_R_SetColor( hcolor ); + } else { + trap_R_SetColor( NULL ); + } + + w = h = cg_crosshairSize.value; + + // pulse the size of the crosshair when picking up items + f = cg.time - cg.itemPickupBlendTime; + if ( f > 0 && f < ITEM_BLOB_TIME ) { + f /= ITEM_BLOB_TIME; + w *= ( 1 + f ); + h *= ( 1 + f ); + } + + x = cg_crosshairX.integer; + y = cg_crosshairY.integer; + CG_AdjustFrom640( &x, &y, &w, &h ); + + ca = cg_drawCrosshair.integer; + if (ca < 0) { + ca = 0; + } + hShader = cgs.media.crosshairShader[ ca % NUM_CROSSHAIRS ]; + + trap_R_DrawStretchPic( x + cg.refdef.x + 0.5 * (cg.refdef.width - w), + y + cg.refdef.y + 0.5 * (cg.refdef.height - h), + w, h, 0, 0, 1, 1, hShader ); +} + +/* +================= +CG_DrawCrosshair3D +================= +*/ +static void CG_DrawCrosshair3D(void) +{ + float w, h; + qhandle_t hShader; + float f; + int ca; + + trace_t trace; + vec3_t endpos; + float stereoSep, zProj, maxdist, xmax; + char rendererinfos[128]; + refEntity_t ent; + + if ( !cg_drawCrosshair.integer ) { + return; + } + + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR) { + return; + } + + if ( cg.renderingThirdPerson ) { + return; + } + + w = h = cg_crosshairSize.value; + + // pulse the size of the crosshair when picking up items + f = cg.time - cg.itemPickupBlendTime; + if ( f > 0 && f < ITEM_BLOB_TIME ) { + f /= ITEM_BLOB_TIME; + w *= ( 1 + f ); + h *= ( 1 + f ); + } + + ca = cg_drawCrosshair.integer; + if (ca < 0) { + ca = 0; + } + hShader = cgs.media.crosshairShader[ ca % NUM_CROSSHAIRS ]; + + // Use a different method rendering the crosshair so players don't see two of them when + // focusing their eyes at distant objects with high stereo separation + // We are going to trace to the next shootable object and place the crosshair in front of it. + + // first get all the important renderer information + trap_Cvar_VariableStringBuffer("r_zProj", rendererinfos, sizeof(rendererinfos)); + zProj = atof(rendererinfos); + trap_Cvar_VariableStringBuffer("r_stereoSeparation", rendererinfos, sizeof(rendererinfos)); + stereoSep = zProj / atof(rendererinfos); + + xmax = zProj * tan(cg.refdef.fov_x * M_PI / 360.0f); + + // let the trace run through until a change in stereo separation of the crosshair becomes less than one pixel. + maxdist = cgs.glconfig.vidWidth * stereoSep * zProj / (2 * xmax); + VectorMA(cg.refdef.vieworg, maxdist, cg.refdef.viewaxis[0], endpos); + CG_Trace(&trace, cg.refdef.vieworg, NULL, NULL, endpos, 0, MASK_SHOT); + + memset(&ent, 0, sizeof(ent)); + ent.reType = RT_SPRITE; + ent.renderfx = RF_DEPTHHACK | RF_CROSSHAIR; + + VectorCopy(trace.endpos, ent.origin); + + // scale the crosshair so it appears the same size for all distances + ent.radius = w / 640 * xmax * trace.fraction * maxdist / zProj; + ent.customShader = hShader; + + trap_R_AddRefEntityToScene(&ent); +} + + + +/* +================= +CG_ScanForCrosshairEntity +================= +*/ +static void CG_ScanForCrosshairEntity( void ) { + trace_t trace; + vec3_t start, end; + int content; + + VectorCopy( cg.refdef.vieworg, start ); + VectorMA( start, 131072, cg.refdef.viewaxis[0], end ); + + CG_Trace( &trace, start, vec3_origin, vec3_origin, end, + cg.snap->ps.clientNum, CONTENTS_SOLID|CONTENTS_BODY ); + if ( trace.entityNum >= MAX_CLIENTS ) { + return; + } + + // if the player is in fog, don't show it + content = trap_CM_PointContents( trace.endpos, 0 ); + if ( content & CONTENTS_FOG ) { + return; + } + + // if the player is invisible, don't show it + if ( cg_entities[ trace.entityNum ].currentState.powerups & ( 1 << PW_INVIS ) ) { + return; + } + + // update the fade timer + cg.crosshairClientNum = trace.entityNum; + cg.crosshairClientTime = cg.time; +} + + +/* +===================== +CG_DrawCrosshairNames +===================== +*/ +static void CG_DrawCrosshairNames( void ) { + float *color; + char *name; + float w; + + if ( !cg_drawCrosshair.integer ) { + return; + } + if ( !cg_drawCrosshairNames.integer ) { + return; + } + if ( cg.renderingThirdPerson ) { + return; + } + + // scan the known entities to see if the crosshair is sighted on one + CG_ScanForCrosshairEntity(); + + // draw the name of the player being looked at + color = CG_FadeColor( cg.crosshairClientTime, 1000 ); + if ( !color ) { + trap_R_SetColor( NULL ); + return; + } + + name = cgs.clientinfo[ cg.crosshairClientNum ].name; +#ifdef MISSIONPACK + color[3] *= 0.5f; + w = CG_Text_Width(name, 0.3f, 0); + CG_Text_Paint( 320 - w / 2, 190, 0.3f, color, name, 0, 0, ITEM_TEXTSTYLE_SHADOWED); +#else + w = CG_DrawStrlen( name ) * BIGCHAR_WIDTH; + CG_DrawBigString( 320 - w / 2, 170, name, color[3] * 0.5f ); +#endif + trap_R_SetColor( NULL ); +} + + +//============================================================================== + +/* +================= +CG_DrawSpectator +================= +*/ +static void CG_DrawSpectator(void) { + CG_DrawBigString(320 - 9 * 8, 440, "SPECTATOR", 1.0F); + if ( cgs.gametype == GT_TOURNAMENT ) { + CG_DrawBigString(320 - 15 * 8, 460, "waiting to play", 1.0F); + } + else if ( cgs.gametype >= GT_TEAM ) { + CG_DrawBigString(320 - 39 * 8, 460, "press ESC and use the JOIN menu to play", 1.0F); + } +} + +/* +================= +CG_DrawVote +================= +*/ +static void CG_DrawVote(void) { + char *s; + int sec; + + if ( !cgs.voteTime ) { + return; + } + + // play a talk beep whenever it is modified + if ( cgs.voteModified ) { + cgs.voteModified = qfalse; + trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); + } + + sec = ( VOTE_TIME - ( cg.time - cgs.voteTime ) ) / 1000; + if ( sec < 0 ) { + sec = 0; + } +#ifdef MISSIONPACK + s = va("VOTE(%i):%s yes:%i no:%i", sec, cgs.voteString, cgs.voteYes, cgs.voteNo); + CG_DrawSmallString( 0, 58, s, 1.0F ); + s = "or press ESC then click Vote"; + CG_DrawSmallString( 0, 58 + SMALLCHAR_HEIGHT + 2, s, 1.0F ); +#else + s = va("VOTE(%i):%s yes:%i no:%i", sec, cgs.voteString, cgs.voteYes, cgs.voteNo ); + CG_DrawSmallString( 0, 58, s, 1.0F ); +#endif +} + +/* +================= +CG_DrawTeamVote +================= +*/ +static void CG_DrawTeamVote(void) { + char *s; + int sec, cs_offset; + + if ( cgs.clientinfo->team == TEAM_RED ) + cs_offset = 0; + else if ( cgs.clientinfo->team == TEAM_BLUE ) + cs_offset = 1; + else + return; + + if ( !cgs.teamVoteTime[cs_offset] ) { + return; + } + + // play a talk beep whenever it is modified + if ( cgs.teamVoteModified[cs_offset] ) { + cgs.teamVoteModified[cs_offset] = qfalse; + trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); + } + + sec = ( VOTE_TIME - ( cg.time - cgs.teamVoteTime[cs_offset] ) ) / 1000; + if ( sec < 0 ) { + sec = 0; + } + s = va("TEAMVOTE(%i):%s yes:%i no:%i", sec, cgs.teamVoteString[cs_offset], + cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset] ); + CG_DrawSmallString( 0, 90, s, 1.0F ); +} + + +static qboolean CG_DrawScoreboard( void ) { +#ifdef MISSIONPACK + static qboolean firstTime = qtrue; + float fade, *fadeColor; + + if (menuScoreboard) { + menuScoreboard->window.flags &= ~WINDOW_FORCED; + } + if (cg_paused.integer) { + cg.deferredPlayerLoading = 0; + firstTime = qtrue; + return qfalse; + } + + // should never happen in Team Arena + if (cgs.gametype == GT_SINGLE_PLAYER && cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { + cg.deferredPlayerLoading = 0; + firstTime = qtrue; + return qfalse; + } + + // don't draw scoreboard during death while warmup up + if ( cg.warmup && !cg.showScores ) { + return qfalse; + } + + if ( cg.showScores || cg.predictedPlayerState.pm_type == PM_DEAD || cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { + fade = 1.0; + fadeColor = colorWhite; + } else { + fadeColor = CG_FadeColor( cg.scoreFadeTime, FADE_TIME ); + if ( !fadeColor ) { + // next time scoreboard comes up, don't print killer + cg.deferredPlayerLoading = 0; + cg.killerName[0] = 0; + firstTime = qtrue; + return qfalse; + } + fade = *fadeColor; + } + + + if (menuScoreboard == NULL) { + if ( cgs.gametype >= GT_TEAM ) { + menuScoreboard = Menus_FindByName("teamscore_menu"); + } else { + menuScoreboard = Menus_FindByName("score_menu"); + } + } + + if (menuScoreboard) { + if (firstTime) { + CG_SetScoreSelection(menuScoreboard); + firstTime = qfalse; + } + Menu_Paint(menuScoreboard, qtrue); + } + + // load any models that have been deferred + if ( ++cg.deferredPlayerLoading > 10 ) { + CG_LoadDeferredPlayers(); + } + + return qtrue; +#else + return CG_DrawOldScoreboard(); +#endif +} + +/* +================= +CG_DrawIntermission +================= +*/ +static void CG_DrawIntermission( void ) { +// int key; +#ifdef MISSIONPACK + //if (cg_singlePlayer.integer) { + // CG_DrawCenterString(); + // return; + //} +#else + if ( cgs.gametype == GT_SINGLE_PLAYER ) { + CG_DrawCenterString(); + return; + } +#endif + cg.scoreFadeTime = cg.time; + cg.scoreBoardShowing = CG_DrawScoreboard(); +} + +/* +================= +CG_DrawFollow +================= +*/ +static qboolean CG_DrawFollow( void ) { + float x; + vec4_t color; + const char *name; + + if ( !(cg.snap->ps.pm_flags & PMF_FOLLOW) ) { + return qfalse; + } + color[0] = 1; + color[1] = 1; + color[2] = 1; + color[3] = 1; + + + CG_DrawBigString( 320 - 9 * 8, 24, "following", 1.0F ); + + name = cgs.clientinfo[ cg.snap->ps.clientNum ].name; + + x = 0.5 * ( 640 - GIANT_WIDTH * CG_DrawStrlen( name ) ); + + CG_DrawStringExt( x, 40, name, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); + + return qtrue; +} + + + +/* +================= +CG_DrawAmmoWarning +================= +*/ +static void CG_DrawAmmoWarning( void ) { + const char *s; + int w; + + if ( cg_drawAmmoWarning.integer == 0 ) { + return; + } + + if ( !cg.lowAmmoWarning ) { + return; + } + + if ( cg.lowAmmoWarning == 2 ) { + s = "OUT OF AMMO"; + } else { + s = "LOW AMMO WARNING"; + } + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; + CG_DrawBigString(320 - w / 2, 64, s, 1.0F); +} + + +#ifdef MISSIONPACK +/* +================= +CG_DrawProxWarning +================= +*/ +static void CG_DrawProxWarning( void ) { + char s [32]; + int w; + static int proxTime; + static int proxCounter; + static int proxTick; + + if( !(cg.snap->ps.eFlags & EF_TICKING ) ) { + proxTime = 0; + return; + } + + if (proxTime == 0) { + proxTime = cg.time + 5000; + proxCounter = 5; + proxTick = 0; + } + + if (cg.time > proxTime) { + proxTick = proxCounter--; + proxTime = cg.time + 1000; + } + + if (proxTick != 0) { + Com_sprintf(s, sizeof(s), "INTERNAL COMBUSTION IN: %i", proxTick); + } else { + Com_sprintf(s, sizeof(s), "YOU HAVE BEEN MINED"); + } + + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; + CG_DrawBigStringColor( 320 - w / 2, 64 + BIGCHAR_HEIGHT, s, g_color_table[ColorIndex(COLOR_RED)] ); +} +#endif + + +/* +================= +CG_DrawWarmup +================= +*/ +static void CG_DrawWarmup( void ) { + int w; + int sec; + int i; + float scale; + clientInfo_t *ci1, *ci2; + int cw; + const char *s; + + sec = cg.warmup; + if ( !sec ) { + return; + } + + if ( sec < 0 ) { + s = "Waiting for players"; + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; + CG_DrawBigString(320 - w / 2, 24, s, 1.0F); + cg.warmupCount = 0; + return; + } + + if (cgs.gametype == GT_TOURNAMENT) { + // find the two active players + ci1 = NULL; + ci2 = NULL; + for ( i = 0 ; i < cgs.maxclients ; i++ ) { + if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_FREE ) { + if ( !ci1 ) { + ci1 = &cgs.clientinfo[i]; + } else { + ci2 = &cgs.clientinfo[i]; + } + } + } + + if ( ci1 && ci2 ) { + s = va( "%s vs %s", ci1->name, ci2->name ); +#ifdef MISSIONPACK + w = CG_Text_Width(s, 0.6f, 0); + CG_Text_Paint(320 - w / 2, 60, 0.6f, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE); +#else + w = CG_DrawStrlen( s ); + if ( w > 640 / GIANT_WIDTH ) { + cw = 640 / w; + } else { + cw = GIANT_WIDTH; + } + CG_DrawStringExt( 320 - w * cw/2, 20,s, colorWhite, + qfalse, qtrue, cw, (int)(cw * 1.5f), 0 ); +#endif + } + } else { + if ( cgs.gametype == GT_FFA ) { + s = "Free For All"; + } else if ( cgs.gametype == GT_TEAM ) { + s = "Team Deathmatch"; + } else if ( cgs.gametype == GT_CTF ) { + s = "Capture the Flag"; +#ifdef MISSIONPACK + } else if ( cgs.gametype == GT_1FCTF ) { + s = "One Flag CTF"; + } else if ( cgs.gametype == GT_OBELISK ) { + s = "Overload"; + } else if ( cgs.gametype == GT_HARVESTER ) { + s = "Harvester"; +#endif + } else { + s = ""; + } +#ifdef MISSIONPACK + w = CG_Text_Width(s, 0.6f, 0); + CG_Text_Paint(320 - w / 2, 90, 0.6f, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE); +#else + w = CG_DrawStrlen( s ); + if ( w > 640 / GIANT_WIDTH ) { + cw = 640 / w; + } else { + cw = GIANT_WIDTH; + } + CG_DrawStringExt( 320 - w * cw/2, 25,s, colorWhite, + qfalse, qtrue, cw, (int)(cw * 1.1f), 0 ); +#endif + } + + sec = ( sec - cg.time ) / 1000; + if ( sec < 0 ) { + cg.warmup = 0; + sec = 0; + } + s = va( "Starts in: %i", sec + 1 ); + if ( sec != cg.warmupCount ) { + cg.warmupCount = sec; + switch ( sec ) { + case 0: + trap_S_StartLocalSound( cgs.media.count1Sound, CHAN_ANNOUNCER ); + break; + case 1: + trap_S_StartLocalSound( cgs.media.count2Sound, CHAN_ANNOUNCER ); + break; + case 2: + trap_S_StartLocalSound( cgs.media.count3Sound, CHAN_ANNOUNCER ); + break; + default: + break; + } + } + scale = 0.45f; + switch ( cg.warmupCount ) { + case 0: + cw = 28; + scale = 0.54f; + break; + case 1: + cw = 24; + scale = 0.51f; + break; + case 2: + cw = 20; + scale = 0.48f; + break; + default: + cw = 16; + scale = 0.45f; + break; + } + +#ifdef MISSIONPACK + w = CG_Text_Width(s, scale, 0); + CG_Text_Paint(320 - w / 2, 125, scale, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE); +#else + w = CG_DrawStrlen( s ); + CG_DrawStringExt( 320 - w * cw/2, 70, s, colorWhite, + qfalse, qtrue, cw, (int)(cw * 1.5), 0 ); +#endif +} + +//================================================================================== +#ifdef MISSIONPACK +/* +================= +CG_DrawTimedMenus +================= +*/ +void CG_DrawTimedMenus( void ) { + if (cg.voiceTime) { + int t = cg.time - cg.voiceTime; + if ( t > 2500 ) { + Menus_CloseByName("voiceMenu"); + trap_Cvar_Set("cl_conXOffset", "0"); + cg.voiceTime = 0; + } + } +} +#endif +/* +================= +CG_Draw2D +================= +*/ +static void CG_Draw2D(stereoFrame_t stereoFrame) +{ +#ifdef MISSIONPACK + if (cgs.orderPending && cg.time > cgs.orderTime) { + CG_CheckOrderPending(); + } +#endif + // if we are taking a levelshot for the menu, don't draw anything + if ( cg.levelShot ) { + return; + } + + if ( cg_draw2D.integer == 0 ) { + return; + } + + if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { + CG_DrawIntermission(); + return; + } + +/* + if (cg.cameraMode) { + return; + } +*/ + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) { + CG_DrawSpectator(); + + if(stereoFrame == STEREO_CENTER) + CG_DrawCrosshair(); + + CG_DrawCrosshairNames(); + } else { + // don't draw any status if dead or the scoreboard is being explicitly shown + if ( !cg.showScores && cg.snap->ps.stats[STAT_HEALTH] > 0 ) { + +#ifdef MISSIONPACK + if ( cg_drawStatus.integer ) { + Menu_PaintAll(); + CG_DrawTimedMenus(); + } +#else + CG_DrawStatusBar(); +#endif + + CG_DrawAmmoWarning(); + +#ifdef MISSIONPACK + CG_DrawProxWarning(); +#endif + if(stereoFrame == STEREO_CENTER) + CG_DrawCrosshair(); + CG_DrawCrosshairNames(); + CG_DrawWeaponSelect(); + +#ifndef MISSIONPACK + CG_DrawHoldableItem(); +#else + //CG_DrawPersistantPowerup(); +#endif + CG_DrawReward(); + } + + if ( cgs.gametype >= GT_TEAM ) { +#ifndef MISSIONPACK + CG_DrawTeamInfo(); +#endif + } + } + + CG_DrawVote(); + CG_DrawTeamVote(); + + CG_DrawLagometer(); + +#ifdef MISSIONPACK + if (!cg_paused.integer) { + CG_DrawUpperRight(stereoFrame); + } +#else + CG_DrawUpperRight(stereoFrame); +#endif + +#ifndef MISSIONPACK + CG_DrawLowerRight(); + CG_DrawLowerLeft(); +#endif + + if ( !CG_DrawFollow() ) { + CG_DrawWarmup(); + } + + // don't draw center string if scoreboard is up + cg.scoreBoardShowing = CG_DrawScoreboard(); + if ( !cg.scoreBoardShowing) { + CG_DrawCenterString(); + } +} + + +static void CG_DrawTourneyScoreboard( void ) { +#ifdef MISSIONPACK +#else + CG_DrawOldTourneyScoreboard(); +#endif +} + +/* +===================== +CG_DrawActive + +Perform all drawing needed to completely fill the screen +===================== +*/ +void CG_DrawActive( stereoFrame_t stereoView ) { + // optionally draw the info screen instead + if ( !cg.snap ) { + CG_DrawInformation(); + return; + } + + // optionally draw the tournement scoreboard instead + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR && + ( cg.snap->ps.pm_flags & PMF_SCOREBOARD ) ) { + CG_DrawTourneyScoreboard(); + return; + } + + // clear around the rendered view if sized down + CG_TileClear(); + + if(stereoView != STEREO_CENTER) + CG_DrawCrosshair3D(); + + // draw 3D view + trap_R_RenderScene( &cg.refdef ); + + // draw status bar and other floating elements + CG_Draw2D(stereoView); +} + + + diff --git a/reaction/engine/code/cgame/cg_drawtools.c b/reaction/engine/code/cgame/cg_drawtools.c new file mode 100644 index 00000000..2651d98d --- /dev/null +++ b/reaction/engine/code/cgame/cg_drawtools.c @@ -0,0 +1,817 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_drawtools.c -- helper functions called by cg_draw, cg_scoreboard, cg_info, etc +#include "cg_local.h" + +/* +================ +CG_AdjustFrom640 + +Adjusted for resolution and screen aspect ratio +================ +*/ +void CG_AdjustFrom640( float *x, float *y, float *w, float *h ) { +#if 0 + // adjust for wide screens + if ( cgs.glconfig.vidWidth * 480 > cgs.glconfig.vidHeight * 640 ) { + *x += 0.5 * ( cgs.glconfig.vidWidth - ( cgs.glconfig.vidHeight * 640 / 480 ) ); + } +#endif + // scale for screen sizes + *x *= cgs.screenXScale; + *y *= cgs.screenYScale; + *w *= cgs.screenXScale; + *h *= cgs.screenYScale; +} + +/* +================ +CG_FillRect + +Coordinates are 640*480 virtual values +================= +*/ +void CG_FillRect( float x, float y, float width, float height, const float *color ) { + trap_R_SetColor( color ); + + CG_AdjustFrom640( &x, &y, &width, &height ); + trap_R_DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cgs.media.whiteShader ); + + trap_R_SetColor( NULL ); +} + +/* +================ +CG_DrawSides + +Coords are virtual 640x480 +================ +*/ +void CG_DrawSides(float x, float y, float w, float h, float size) { + CG_AdjustFrom640( &x, &y, &w, &h ); + size *= cgs.screenXScale; + trap_R_DrawStretchPic( x, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader ); + trap_R_DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader ); +} + +void CG_DrawTopBottom(float x, float y, float w, float h, float size) { + CG_AdjustFrom640( &x, &y, &w, &h ); + size *= cgs.screenYScale; + trap_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, cgs.media.whiteShader ); + trap_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, cgs.media.whiteShader ); +} +/* +================ +UI_DrawRect + +Coordinates are 640*480 virtual values +================= +*/ +void CG_DrawRect( float x, float y, float width, float height, float size, const float *color ) { + trap_R_SetColor( color ); + + CG_DrawTopBottom(x, y, width, height, size); + CG_DrawSides(x, y, width, height, size); + + trap_R_SetColor( NULL ); +} + + + +/* +================ +CG_DrawPic + +Coordinates are 640*480 virtual values +================= +*/ +void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) { + CG_AdjustFrom640( &x, &y, &width, &height ); + trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader ); +} + + + +/* +=============== +CG_DrawChar + +Coordinates and size in 640*480 virtual screen size +=============== +*/ +void CG_DrawChar( int x, int y, int width, int height, int ch ) { + int row, col; + float frow, fcol; + float size; + float ax, ay, aw, ah; + + ch &= 255; + + if ( ch == ' ' ) { + return; + } + + ax = x; + ay = y; + aw = width; + ah = height; + CG_AdjustFrom640( &ax, &ay, &aw, &ah ); + + row = ch>>4; + col = ch&15; + + frow = row*0.0625; + fcol = col*0.0625; + size = 0.0625; + + trap_R_DrawStretchPic( ax, ay, aw, ah, + fcol, frow, + fcol + size, frow + size, + cgs.media.charsetShader ); +} + + +/* +================== +CG_DrawStringExt + +Draws a multi-colored string with a drop shadow, optionally forcing +to a fixed color. + +Coordinates are at 640 by 480 virtual resolution +================== +*/ +void CG_DrawStringExt( int x, int y, const char *string, const float *setColor, + qboolean forceColor, qboolean shadow, int charWidth, int charHeight, int maxChars ) { + vec4_t color; + const char *s; + int xx; + int cnt; + + if (maxChars <= 0) + maxChars = 32767; // do them all! + + // draw the drop shadow + if (shadow) { + color[0] = color[1] = color[2] = 0; + color[3] = setColor[3]; + trap_R_SetColor( color ); + s = string; + xx = x; + cnt = 0; + while ( *s && cnt < maxChars) { + if ( Q_IsColorString( s ) ) { + s += 2; + continue; + } + CG_DrawChar( xx + 2, y + 2, charWidth, charHeight, *s ); + cnt++; + xx += charWidth; + s++; + } + } + + // draw the colored text + s = string; + xx = x; + cnt = 0; + trap_R_SetColor( setColor ); + while ( *s && cnt < maxChars) { + if ( Q_IsColorString( s ) ) { + if ( !forceColor ) { + memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) ); + color[3] = setColor[3]; + trap_R_SetColor( color ); + } + s += 2; + continue; + } + CG_DrawChar( xx, y, charWidth, charHeight, *s ); + xx += charWidth; + cnt++; + s++; + } + trap_R_SetColor( NULL ); +} + +void CG_DrawBigString( int x, int y, const char *s, float alpha ) { + float color[4]; + + color[0] = color[1] = color[2] = 1.0; + color[3] = alpha; + CG_DrawStringExt( x, y, s, color, qfalse, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0 ); +} + +void CG_DrawBigStringColor( int x, int y, const char *s, vec4_t color ) { + CG_DrawStringExt( x, y, s, color, qtrue, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0 ); +} + +void CG_DrawSmallString( int x, int y, const char *s, float alpha ) { + float color[4]; + + color[0] = color[1] = color[2] = 1.0; + color[3] = alpha; + CG_DrawStringExt( x, y, s, color, qfalse, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 ); +} + +void CG_DrawSmallStringColor( int x, int y, const char *s, vec4_t color ) { + CG_DrawStringExt( x, y, s, color, qtrue, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 ); +} + +/* +================= +CG_DrawStrlen + +Returns character count, skiping color escape codes +================= +*/ +int CG_DrawStrlen( const char *str ) { + const char *s = str; + int count = 0; + + while ( *s ) { + if ( Q_IsColorString( s ) ) { + s += 2; + } else { + count++; + s++; + } + } + + return count; +} + +/* +============= +CG_TileClearBox + +This repeats a 64*64 tile graphic to fill the screen around a sized down +refresh window. +============= +*/ +static void CG_TileClearBox( int x, int y, int w, int h, qhandle_t hShader ) { + float s1, t1, s2, t2; + + s1 = x/64.0; + t1 = y/64.0; + s2 = (x+w)/64.0; + t2 = (y+h)/64.0; + trap_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, hShader ); +} + + + +/* +============== +CG_TileClear + +Clear around a sized down screen +============== +*/ +void CG_TileClear( void ) { + int top, bottom, left, right; + int w, h; + + w = cgs.glconfig.vidWidth; + h = cgs.glconfig.vidHeight; + + if ( cg.refdef.x == 0 && cg.refdef.y == 0 && + cg.refdef.width == w && cg.refdef.height == h ) { + return; // full screen rendering + } + + top = cg.refdef.y; + bottom = top + cg.refdef.height-1; + left = cg.refdef.x; + right = left + cg.refdef.width-1; + + // clear above view screen + CG_TileClearBox( 0, 0, w, top, cgs.media.backTileShader ); + + // clear below view screen + CG_TileClearBox( 0, bottom, w, h - bottom, cgs.media.backTileShader ); + + // clear left of view screen + CG_TileClearBox( 0, top, left, bottom - top + 1, cgs.media.backTileShader ); + + // clear right of view screen + CG_TileClearBox( right, top, w - right, bottom - top + 1, cgs.media.backTileShader ); +} + + + +/* +================ +CG_FadeColor +================ +*/ +float *CG_FadeColor( int startMsec, int totalMsec ) { + static vec4_t color; + int t; + + if ( startMsec == 0 ) { + return NULL; + } + + t = cg.time - startMsec; + + if ( t >= totalMsec ) { + return NULL; + } + + // fade out + if ( totalMsec - t < FADE_TIME ) { + color[3] = ( totalMsec - t ) * 1.0/FADE_TIME; + } else { + color[3] = 1.0; + } + color[0] = color[1] = color[2] = 1; + + return color; +} + + +/* +================ +CG_TeamColor +================ +*/ +float *CG_TeamColor( int team ) { + static vec4_t red = {1, 0.2f, 0.2f, 1}; + static vec4_t blue = {0.2f, 0.2f, 1, 1}; + static vec4_t other = {1, 1, 1, 1}; + static vec4_t spectator = {0.7f, 0.7f, 0.7f, 1}; + + switch ( team ) { + case TEAM_RED: + return red; + case TEAM_BLUE: + return blue; + case TEAM_SPECTATOR: + return spectator; + default: + return other; + } +} + + + +/* +================= +CG_GetColorForHealth +================= +*/ +void CG_GetColorForHealth( int health, int armor, vec4_t hcolor ) { + int count; + int max; + + // calculate the total points of damage that can + // be sustained at the current health / armor level + if ( health <= 0 ) { + VectorClear( hcolor ); // black + hcolor[3] = 1; + return; + } + count = armor; + max = health * ARMOR_PROTECTION / ( 1.0 - ARMOR_PROTECTION ); + if ( max < count ) { + count = max; + } + health += count; + + // set the color based on health + hcolor[0] = 1.0; + hcolor[3] = 1.0; + if ( health >= 100 ) { + hcolor[2] = 1.0; + } else if ( health < 66 ) { + hcolor[2] = 0; + } else { + hcolor[2] = ( health - 66 ) / 33.0; + } + + if ( health > 60 ) { + hcolor[1] = 1.0; + } else if ( health < 30 ) { + hcolor[1] = 0; + } else { + hcolor[1] = ( health - 30 ) / 30.0; + } +} + +/* +================= +CG_ColorForHealth +================= +*/ +void CG_ColorForHealth( vec4_t hcolor ) { + + CG_GetColorForHealth( cg.snap->ps.stats[STAT_HEALTH], + cg.snap->ps.stats[STAT_ARMOR], hcolor ); +} + + +/* +================= +UI_DrawProportionalString2 +================= +*/ +static int propMap[128][3] = { +{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, +{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, + +{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, +{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, + +{0, 0, PROP_SPACE_WIDTH}, // SPACE +{11, 122, 7}, // ! +{154, 181, 14}, // " +{55, 122, 17}, // # +{79, 122, 18}, // $ +{101, 122, 23}, // % +{153, 122, 18}, // & +{9, 93, 7}, // ' +{207, 122, 8}, // ( +{230, 122, 9}, // ) +{177, 122, 18}, // * +{30, 152, 18}, // + +{85, 181, 7}, // , +{34, 93, 11}, // - +{110, 181, 6}, // . +{130, 152, 14}, // / + +{22, 64, 17}, // 0 +{41, 64, 12}, // 1 +{58, 64, 17}, // 2 +{78, 64, 18}, // 3 +{98, 64, 19}, // 4 +{120, 64, 18}, // 5 +{141, 64, 18}, // 6 +{204, 64, 16}, // 7 +{162, 64, 17}, // 8 +{182, 64, 18}, // 9 +{59, 181, 7}, // : +{35,181, 7}, // ; +{203, 152, 14}, // < +{56, 93, 14}, // = +{228, 152, 14}, // > +{177, 181, 18}, // ? + +{28, 122, 22}, // @ +{5, 4, 18}, // A +{27, 4, 18}, // B +{48, 4, 18}, // C +{69, 4, 17}, // D +{90, 4, 13}, // E +{106, 4, 13}, // F +{121, 4, 18}, // G +{143, 4, 17}, // H +{164, 4, 8}, // I +{175, 4, 16}, // J +{195, 4, 18}, // K +{216, 4, 12}, // L +{230, 4, 23}, // M +{6, 34, 18}, // N +{27, 34, 18}, // O + +{48, 34, 18}, // P +{68, 34, 18}, // Q +{90, 34, 17}, // R +{110, 34, 18}, // S +{130, 34, 14}, // T +{146, 34, 18}, // U +{166, 34, 19}, // V +{185, 34, 29}, // W +{215, 34, 18}, // X +{234, 34, 18}, // Y +{5, 64, 14}, // Z +{60, 152, 7}, // [ +{106, 151, 13}, // '\' +{83, 152, 7}, // ] +{128, 122, 17}, // ^ +{4, 152, 21}, // _ + +{134, 181, 5}, // ' +{5, 4, 18}, // A +{27, 4, 18}, // B +{48, 4, 18}, // C +{69, 4, 17}, // D +{90, 4, 13}, // E +{106, 4, 13}, // F +{121, 4, 18}, // G +{143, 4, 17}, // H +{164, 4, 8}, // I +{175, 4, 16}, // J +{195, 4, 18}, // K +{216, 4, 12}, // L +{230, 4, 23}, // M +{6, 34, 18}, // N +{27, 34, 18}, // O + +{48, 34, 18}, // P +{68, 34, 18}, // Q +{90, 34, 17}, // R +{110, 34, 18}, // S +{130, 34, 14}, // T +{146, 34, 18}, // U +{166, 34, 19}, // V +{185, 34, 29}, // W +{215, 34, 18}, // X +{234, 34, 18}, // Y +{5, 64, 14}, // Z +{153, 152, 13}, // { +{11, 181, 5}, // | +{180, 152, 13}, // } +{79, 93, 17}, // ~ +{0, 0, -1} // DEL +}; + +static int propMapB[26][3] = { +{11, 12, 33}, +{49, 12, 31}, +{85, 12, 31}, +{120, 12, 30}, +{156, 12, 21}, +{183, 12, 21}, +{207, 12, 32}, + +{13, 55, 30}, +{49, 55, 13}, +{66, 55, 29}, +{101, 55, 31}, +{135, 55, 21}, +{158, 55, 40}, +{204, 55, 32}, + +{12, 97, 31}, +{48, 97, 31}, +{82, 97, 30}, +{118, 97, 30}, +{153, 97, 30}, +{185, 97, 25}, +{213, 97, 30}, + +{11, 139, 32}, +{42, 139, 51}, +{93, 139, 32}, +{126, 139, 31}, +{158, 139, 25}, +}; + +#define PROPB_GAP_WIDTH 4 +#define PROPB_SPACE_WIDTH 12 +#define PROPB_HEIGHT 36 + +/* +================= +UI_DrawBannerString +================= +*/ +static void UI_DrawBannerString2( int x, int y, const char* str, vec4_t color ) +{ + const char* s; + unsigned char ch; + float ax; + float ay; + float aw; + float ah; + float frow; + float fcol; + float fwidth; + float fheight; + + // draw the colored text + trap_R_SetColor( color ); + + ax = x * cgs.screenXScale + cgs.screenXBias; + ay = y * cgs.screenXScale; + + s = str; + while ( *s ) + { + ch = *s & 127; + if ( ch == ' ' ) { + ax += ((float)PROPB_SPACE_WIDTH + (float)PROPB_GAP_WIDTH)* cgs.screenXScale; + } + else if ( ch >= 'A' && ch <= 'Z' ) { + ch -= 'A'; + fcol = (float)propMapB[ch][0] / 256.0f; + frow = (float)propMapB[ch][1] / 256.0f; + fwidth = (float)propMapB[ch][2] / 256.0f; + fheight = (float)PROPB_HEIGHT / 256.0f; + aw = (float)propMapB[ch][2] * cgs.screenXScale; + ah = (float)PROPB_HEIGHT * cgs.screenXScale; + trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol+fwidth, frow+fheight, cgs.media.charsetPropB ); + ax += (aw + (float)PROPB_GAP_WIDTH * cgs.screenXScale); + } + s++; + } + + trap_R_SetColor( NULL ); +} + +void UI_DrawBannerString( int x, int y, const char* str, int style, vec4_t color ) { + const char * s; + int ch; + int width; + vec4_t drawcolor; + + // find the width of the drawn text + s = str; + width = 0; + while ( *s ) { + ch = *s; + if ( ch == ' ' ) { + width += PROPB_SPACE_WIDTH; + } + else if ( ch >= 'A' && ch <= 'Z' ) { + width += propMapB[ch - 'A'][2] + PROPB_GAP_WIDTH; + } + s++; + } + width -= PROPB_GAP_WIDTH; + + switch( style & UI_FORMATMASK ) { + case UI_CENTER: + x -= width / 2; + break; + + case UI_RIGHT: + x -= width; + break; + + case UI_LEFT: + default: + break; + } + + if ( style & UI_DROPSHADOW ) { + drawcolor[0] = drawcolor[1] = drawcolor[2] = 0; + drawcolor[3] = color[3]; + UI_DrawBannerString2( x+2, y+2, str, drawcolor ); + } + + UI_DrawBannerString2( x, y, str, color ); +} + + +int UI_ProportionalStringWidth( const char* str ) { + const char * s; + int ch; + int charWidth; + int width; + + s = str; + width = 0; + while ( *s ) { + ch = *s & 127; + charWidth = propMap[ch][2]; + if ( charWidth != -1 ) { + width += charWidth; + width += PROP_GAP_WIDTH; + } + s++; + } + + width -= PROP_GAP_WIDTH; + return width; +} + +static void UI_DrawProportionalString2( int x, int y, const char* str, vec4_t color, float sizeScale, qhandle_t charset ) +{ + const char* s; + unsigned char ch; + float ax; + float ay; + float aw; + float ah; + float frow; + float fcol; + float fwidth; + float fheight; + + // draw the colored text + trap_R_SetColor( color ); + + ax = x * cgs.screenXScale + cgs.screenXBias; + ay = y * cgs.screenXScale; + + s = str; + while ( *s ) + { + ch = *s & 127; + if ( ch == ' ' ) { + aw = (float)PROP_SPACE_WIDTH * cgs.screenXScale * sizeScale; + } else if ( propMap[ch][2] != -1 ) { + fcol = (float)propMap[ch][0] / 256.0f; + frow = (float)propMap[ch][1] / 256.0f; + fwidth = (float)propMap[ch][2] / 256.0f; + fheight = (float)PROP_HEIGHT / 256.0f; + aw = (float)propMap[ch][2] * cgs.screenXScale * sizeScale; + ah = (float)PROP_HEIGHT * cgs.screenXScale * sizeScale; + trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol+fwidth, frow+fheight, charset ); + } else { + aw = 0; + } + + ax += (aw + (float)PROP_GAP_WIDTH * cgs.screenXScale * sizeScale); + s++; + } + + trap_R_SetColor( NULL ); +} + +/* +================= +UI_ProportionalSizeScale +================= +*/ +float UI_ProportionalSizeScale( int style ) { + if( style & UI_SMALLFONT ) { + return 0.75; + } + + return 1.00; +} + + +/* +================= +UI_DrawProportionalString +================= +*/ +void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color ) { + vec4_t drawcolor; + int width; + float sizeScale; + + sizeScale = UI_ProportionalSizeScale( style ); + + switch( style & UI_FORMATMASK ) { + case UI_CENTER: + width = UI_ProportionalStringWidth( str ) * sizeScale; + x -= width / 2; + break; + + case UI_RIGHT: + width = UI_ProportionalStringWidth( str ) * sizeScale; + x -= width; + break; + + case UI_LEFT: + default: + break; + } + + if ( style & UI_DROPSHADOW ) { + drawcolor[0] = drawcolor[1] = drawcolor[2] = 0; + drawcolor[3] = color[3]; + UI_DrawProportionalString2( x+2, y+2, str, drawcolor, sizeScale, cgs.media.charsetProp ); + } + + if ( style & UI_INVERSE ) { + drawcolor[0] = color[0] * 0.8; + drawcolor[1] = color[1] * 0.8; + drawcolor[2] = color[2] * 0.8; + drawcolor[3] = color[3]; + UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, cgs.media.charsetProp ); + return; + } + + if ( style & UI_PULSE ) { + drawcolor[0] = color[0] * 0.8; + drawcolor[1] = color[1] * 0.8; + drawcolor[2] = color[2] * 0.8; + drawcolor[3] = color[3]; + UI_DrawProportionalString2( x, y, str, color, sizeScale, cgs.media.charsetProp ); + + drawcolor[0] = color[0]; + drawcolor[1] = color[1]; + drawcolor[2] = color[2]; + drawcolor[3] = 0.5 + 0.5 * sin( cg.time / PULSE_DIVISOR ); + UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, cgs.media.charsetPropGlow ); + return; + } + + UI_DrawProportionalString2( x, y, str, color, sizeScale, cgs.media.charsetProp ); +} diff --git a/reaction/engine/code/cgame/cg_effects.c b/reaction/engine/code/cgame/cg_effects.c new file mode 100644 index 00000000..99f64995 --- /dev/null +++ b/reaction/engine/code/cgame/cg_effects.c @@ -0,0 +1,718 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_effects.c -- these functions generate localentities, usually as a result +// of event processing + +#include "cg_local.h" + + +/* +================== +CG_BubbleTrail + +Bullets shot underwater +================== +*/ +void CG_BubbleTrail( vec3_t start, vec3_t end, float spacing ) { + vec3_t move; + vec3_t vec; + float len; + int i; + + if ( cg_noProjectileTrail.integer ) { + return; + } + + VectorCopy (start, move); + VectorSubtract (end, start, vec); + len = VectorNormalize (vec); + + // advance a random amount first + i = rand() % (int)spacing; + VectorMA( move, i, vec, move ); + + VectorScale (vec, spacing, vec); + + for ( ; i < len; i += spacing ) { + localEntity_t *le; + refEntity_t *re; + + le = CG_AllocLocalEntity(); + le->leFlags = LEF_PUFF_DONT_SCALE; + le->leType = LE_MOVE_SCALE_FADE; + le->startTime = cg.time; + le->endTime = cg.time + 1000 + random() * 250; + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + + re = &le->refEntity; + re->shaderTime = cg.time / 1000.0f; + + re->reType = RT_SPRITE; + re->rotation = 0; + re->radius = 3; + re->customShader = cgs.media.waterBubbleShader; + re->shaderRGBA[0] = 0xff; + re->shaderRGBA[1] = 0xff; + re->shaderRGBA[2] = 0xff; + re->shaderRGBA[3] = 0xff; + + le->color[3] = 1.0; + + le->pos.trType = TR_LINEAR; + le->pos.trTime = cg.time; + VectorCopy( move, le->pos.trBase ); + le->pos.trDelta[0] = crandom()*5; + le->pos.trDelta[1] = crandom()*5; + le->pos.trDelta[2] = crandom()*5 + 6; + + VectorAdd (move, vec, move); + } +} + +/* +===================== +CG_SmokePuff + +Adds a smoke puff or blood trail localEntity. +===================== +*/ +localEntity_t *CG_SmokePuff( const vec3_t p, const vec3_t vel, + float radius, + float r, float g, float b, float a, + float duration, + int startTime, + int fadeInTime, + int leFlags, + qhandle_t hShader ) { + static int seed = 0x92; + localEntity_t *le; + refEntity_t *re; +// int fadeInTime = startTime + duration / 2; + + le = CG_AllocLocalEntity(); + le->leFlags = leFlags; + le->radius = radius; + + re = &le->refEntity; + re->rotation = Q_random( &seed ) * 360; + re->radius = radius; + re->shaderTime = startTime / 1000.0f; + + le->leType = LE_MOVE_SCALE_FADE; + le->startTime = startTime; + le->fadeInTime = fadeInTime; + le->endTime = startTime + duration; + if ( fadeInTime > startTime ) { + le->lifeRate = 1.0 / ( le->endTime - le->fadeInTime ); + } + else { + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + } + le->color[0] = r; + le->color[1] = g; + le->color[2] = b; + le->color[3] = a; + + + le->pos.trType = TR_LINEAR; + le->pos.trTime = startTime; + VectorCopy( vel, le->pos.trDelta ); + VectorCopy( p, le->pos.trBase ); + + VectorCopy( p, re->origin ); + re->customShader = hShader; + + // rage pro can't alpha fade, so use a different shader + if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) { + re->customShader = cgs.media.smokePuffRageProShader; + re->shaderRGBA[0] = 0xff; + re->shaderRGBA[1] = 0xff; + re->shaderRGBA[2] = 0xff; + re->shaderRGBA[3] = 0xff; + } else { + re->shaderRGBA[0] = le->color[0] * 0xff; + re->shaderRGBA[1] = le->color[1] * 0xff; + re->shaderRGBA[2] = le->color[2] * 0xff; + re->shaderRGBA[3] = 0xff; + } + + re->reType = RT_SPRITE; + re->radius = le->radius; + + return le; +} + +/* +================== +CG_SpawnEffect + +Player teleporting in or out +================== +*/ +void CG_SpawnEffect( vec3_t org ) { + localEntity_t *le; + refEntity_t *re; + + le = CG_AllocLocalEntity(); + le->leFlags = 0; + le->leType = LE_FADE_RGB; + le->startTime = cg.time; + le->endTime = cg.time + 500; + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + + le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; + + re = &le->refEntity; + + re->reType = RT_MODEL; + re->shaderTime = cg.time / 1000.0f; + +#ifndef MISSIONPACK + re->customShader = cgs.media.teleportEffectShader; +#endif + re->hModel = cgs.media.teleportEffectModel; + AxisClear( re->axis ); + + VectorCopy( org, re->origin ); +#ifdef MISSIONPACK + re->origin[2] += 16; +#else + re->origin[2] -= 24; +#endif +} + + +#ifdef MISSIONPACK +/* +=============== +CG_LightningBoltBeam +=============== +*/ +void CG_LightningBoltBeam( vec3_t start, vec3_t end ) { + localEntity_t *le; + refEntity_t *beam; + + le = CG_AllocLocalEntity(); + le->leFlags = 0; + le->leType = LE_SHOWREFENTITY; + le->startTime = cg.time; + le->endTime = cg.time + 50; + + beam = &le->refEntity; + + VectorCopy( start, beam->origin ); + // this is the end point + VectorCopy( end, beam->oldorigin ); + + beam->reType = RT_LIGHTNING; + beam->customShader = cgs.media.lightningShader; +} + +/* +================== +CG_KamikazeEffect +================== +*/ +void CG_KamikazeEffect( vec3_t org ) { + localEntity_t *le; + refEntity_t *re; + + le = CG_AllocLocalEntity(); + le->leFlags = 0; + le->leType = LE_KAMIKAZE; + le->startTime = cg.time; + le->endTime = cg.time + 3000;//2250; + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + + le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; + + VectorClear(le->angles.trBase); + + re = &le->refEntity; + + re->reType = RT_MODEL; + re->shaderTime = cg.time / 1000.0f; + + re->hModel = cgs.media.kamikazeEffectModel; + + VectorCopy( org, re->origin ); + +} + +/* +================== +CG_ObeliskExplode +================== +*/ +void CG_ObeliskExplode( vec3_t org, int entityNum ) { + localEntity_t *le; + vec3_t origin; + + // create an explosion + VectorCopy( org, origin ); + origin[2] += 64; + le = CG_MakeExplosion( origin, vec3_origin, + cgs.media.dishFlashModel, + cgs.media.rocketExplosionShader, + 600, qtrue ); + le->light = 300; + le->lightColor[0] = 1; + le->lightColor[1] = 0.75; + le->lightColor[2] = 0.0; +} + +/* +================== +CG_ObeliskPain +================== +*/ +void CG_ObeliskPain( vec3_t org ) { + float r; + sfxHandle_t sfx; + + // hit sound + r = rand() & 3; + if ( r < 2 ) { + sfx = cgs.media.obeliskHitSound1; + } else if ( r == 2 ) { + sfx = cgs.media.obeliskHitSound2; + } else { + sfx = cgs.media.obeliskHitSound3; + } + trap_S_StartSound ( org, ENTITYNUM_NONE, CHAN_BODY, sfx ); +} + + +/* +================== +CG_InvulnerabilityImpact +================== +*/ +void CG_InvulnerabilityImpact( vec3_t org, vec3_t angles ) { + localEntity_t *le; + refEntity_t *re; + int r; + sfxHandle_t sfx; + + le = CG_AllocLocalEntity(); + le->leFlags = 0; + le->leType = LE_INVULIMPACT; + le->startTime = cg.time; + le->endTime = cg.time + 1000; + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + + le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; + + re = &le->refEntity; + + re->reType = RT_MODEL; + re->shaderTime = cg.time / 1000.0f; + + re->hModel = cgs.media.invulnerabilityImpactModel; + + VectorCopy( org, re->origin ); + AnglesToAxis( angles, re->axis ); + + r = rand() & 3; + if ( r < 2 ) { + sfx = cgs.media.invulnerabilityImpactSound1; + } else if ( r == 2 ) { + sfx = cgs.media.invulnerabilityImpactSound2; + } else { + sfx = cgs.media.invulnerabilityImpactSound3; + } + trap_S_StartSound (org, ENTITYNUM_NONE, CHAN_BODY, sfx ); +} + +/* +================== +CG_InvulnerabilityJuiced +================== +*/ +void CG_InvulnerabilityJuiced( vec3_t org ) { + localEntity_t *le; + refEntity_t *re; + vec3_t angles; + + le = CG_AllocLocalEntity(); + le->leFlags = 0; + le->leType = LE_INVULJUICED; + le->startTime = cg.time; + le->endTime = cg.time + 10000; + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + + le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; + + re = &le->refEntity; + + re->reType = RT_MODEL; + re->shaderTime = cg.time / 1000.0f; + + re->hModel = cgs.media.invulnerabilityJuicedModel; + + VectorCopy( org, re->origin ); + VectorClear(angles); + AnglesToAxis( angles, re->axis ); + + trap_S_StartSound (org, ENTITYNUM_NONE, CHAN_BODY, cgs.media.invulnerabilityJuicedSound ); +} + +#endif + +/* +================== +CG_ScorePlum +================== +*/ +void CG_ScorePlum( int client, vec3_t org, int score ) { + localEntity_t *le; + refEntity_t *re; + vec3_t angles; + static vec3_t lastPos; + + // only visualize for the client that scored + if (client != cg.predictedPlayerState.clientNum || cg_scorePlum.integer == 0) { + return; + } + + le = CG_AllocLocalEntity(); + le->leFlags = 0; + le->leType = LE_SCOREPLUM; + le->startTime = cg.time; + le->endTime = cg.time + 4000; + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + + + le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; + le->radius = score; + + VectorCopy( org, le->pos.trBase ); + if (org[2] >= lastPos[2] - 20 && org[2] <= lastPos[2] + 20) { + le->pos.trBase[2] -= 20; + } + + //CG_Printf( "Plum origin %i %i %i -- %i\n", (int)org[0], (int)org[1], (int)org[2], (int)Distance(org, lastPos)); + VectorCopy(org, lastPos); + + + re = &le->refEntity; + + re->reType = RT_SPRITE; + re->radius = 16; + + VectorClear(angles); + AnglesToAxis( angles, re->axis ); +} + + +/* +==================== +CG_MakeExplosion +==================== +*/ +localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir, + qhandle_t hModel, qhandle_t shader, + int msec, qboolean isSprite ) { + float ang; + localEntity_t *ex; + int offset; + vec3_t tmpVec, newOrigin; + + if ( msec <= 0 ) { + CG_Error( "CG_MakeExplosion: msec = %i", msec ); + } + + // skew the time a bit so they aren't all in sync + offset = rand() & 63; + + ex = CG_AllocLocalEntity(); + if ( isSprite ) { + ex->leType = LE_SPRITE_EXPLOSION; + + // randomly rotate sprite orientation + ex->refEntity.rotation = rand() % 360; + VectorScale( dir, 16, tmpVec ); + VectorAdd( tmpVec, origin, newOrigin ); + } else { + ex->leType = LE_EXPLOSION; + VectorCopy( origin, newOrigin ); + + // set axis with random rotate + if ( !dir ) { + AxisClear( ex->refEntity.axis ); + } else { + ang = rand() % 360; + VectorCopy( dir, ex->refEntity.axis[0] ); + RotateAroundDirection( ex->refEntity.axis, ang ); + } + } + + ex->startTime = cg.time - offset; + ex->endTime = ex->startTime + msec; + + // bias the time so all shader effects start correctly + ex->refEntity.shaderTime = ex->startTime / 1000.0f; + + ex->refEntity.hModel = hModel; + ex->refEntity.customShader = shader; + + // set origin + VectorCopy( newOrigin, ex->refEntity.origin ); + VectorCopy( newOrigin, ex->refEntity.oldorigin ); + + ex->color[0] = ex->color[1] = ex->color[2] = 1.0; + + return ex; +} + + +/* +================= +CG_Bleed + +This is the spurt of blood when a character gets hit +================= +*/ +void CG_Bleed( vec3_t origin, int entityNum ) { + localEntity_t *ex; + + if ( !cg_blood.integer ) { + return; + } + + ex = CG_AllocLocalEntity(); + ex->leType = LE_EXPLOSION; + + ex->startTime = cg.time; + ex->endTime = ex->startTime + 500; + + VectorCopy ( origin, ex->refEntity.origin); + ex->refEntity.reType = RT_SPRITE; + ex->refEntity.rotation = rand() % 360; + ex->refEntity.radius = 24; + + ex->refEntity.customShader = cgs.media.bloodExplosionShader; + + // don't show player's own blood in view + if ( entityNum == cg.snap->ps.clientNum ) { + ex->refEntity.renderfx |= RF_THIRD_PERSON; + } +} + + + +/* +================== +CG_LaunchGib +================== +*/ +void CG_LaunchGib( vec3_t origin, vec3_t velocity, qhandle_t hModel ) { + localEntity_t *le; + refEntity_t *re; + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + le->leType = LE_FRAGMENT; + le->startTime = cg.time; + le->endTime = le->startTime + 5000 + random() * 3000; + + VectorCopy( origin, re->origin ); + AxisCopy( axisDefault, re->axis ); + re->hModel = hModel; + + le->pos.trType = TR_GRAVITY; + VectorCopy( origin, le->pos.trBase ); + VectorCopy( velocity, le->pos.trDelta ); + le->pos.trTime = cg.time; + + le->bounceFactor = 0.6f; + + le->leBounceSoundType = LEBS_BLOOD; + le->leMarkType = LEMT_BLOOD; +} + +/* +=================== +CG_GibPlayer + +Generated a bunch of gibs launching out from the bodies location +=================== +*/ +#define GIB_VELOCITY 250 +#define GIB_JUMP 250 +void CG_GibPlayer( vec3_t playerOrigin ) { + vec3_t origin, velocity; + + if ( !cg_blood.integer ) { + return; + } + + VectorCopy( playerOrigin, origin ); + velocity[0] = crandom()*GIB_VELOCITY; + velocity[1] = crandom()*GIB_VELOCITY; + velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; + if ( rand() & 1 ) { + CG_LaunchGib( origin, velocity, cgs.media.gibSkull ); + } else { + CG_LaunchGib( origin, velocity, cgs.media.gibBrain ); + } + + // allow gibs to be turned off for speed + if ( !cg_gibs.integer ) { + return; + } + + VectorCopy( playerOrigin, origin ); + velocity[0] = crandom()*GIB_VELOCITY; + velocity[1] = crandom()*GIB_VELOCITY; + velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; + CG_LaunchGib( origin, velocity, cgs.media.gibAbdomen ); + + VectorCopy( playerOrigin, origin ); + velocity[0] = crandom()*GIB_VELOCITY; + velocity[1] = crandom()*GIB_VELOCITY; + velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; + CG_LaunchGib( origin, velocity, cgs.media.gibArm ); + + VectorCopy( playerOrigin, origin ); + velocity[0] = crandom()*GIB_VELOCITY; + velocity[1] = crandom()*GIB_VELOCITY; + velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; + CG_LaunchGib( origin, velocity, cgs.media.gibChest ); + + VectorCopy( playerOrigin, origin ); + velocity[0] = crandom()*GIB_VELOCITY; + velocity[1] = crandom()*GIB_VELOCITY; + velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; + CG_LaunchGib( origin, velocity, cgs.media.gibFist ); + + VectorCopy( playerOrigin, origin ); + velocity[0] = crandom()*GIB_VELOCITY; + velocity[1] = crandom()*GIB_VELOCITY; + velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; + CG_LaunchGib( origin, velocity, cgs.media.gibFoot ); + + VectorCopy( playerOrigin, origin ); + velocity[0] = crandom()*GIB_VELOCITY; + velocity[1] = crandom()*GIB_VELOCITY; + velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; + CG_LaunchGib( origin, velocity, cgs.media.gibForearm ); + + VectorCopy( playerOrigin, origin ); + velocity[0] = crandom()*GIB_VELOCITY; + velocity[1] = crandom()*GIB_VELOCITY; + velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; + CG_LaunchGib( origin, velocity, cgs.media.gibIntestine ); + + VectorCopy( playerOrigin, origin ); + velocity[0] = crandom()*GIB_VELOCITY; + velocity[1] = crandom()*GIB_VELOCITY; + velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; + CG_LaunchGib( origin, velocity, cgs.media.gibLeg ); + + VectorCopy( playerOrigin, origin ); + velocity[0] = crandom()*GIB_VELOCITY; + velocity[1] = crandom()*GIB_VELOCITY; + velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; + CG_LaunchGib( origin, velocity, cgs.media.gibLeg ); +} + +/* +================== +CG_LaunchGib +================== +*/ +void CG_LaunchExplode( vec3_t origin, vec3_t velocity, qhandle_t hModel ) { + localEntity_t *le; + refEntity_t *re; + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + le->leType = LE_FRAGMENT; + le->startTime = cg.time; + le->endTime = le->startTime + 10000 + random() * 6000; + + VectorCopy( origin, re->origin ); + AxisCopy( axisDefault, re->axis ); + re->hModel = hModel; + + le->pos.trType = TR_GRAVITY; + VectorCopy( origin, le->pos.trBase ); + VectorCopy( velocity, le->pos.trDelta ); + le->pos.trTime = cg.time; + + le->bounceFactor = 0.1f; + + le->leBounceSoundType = LEBS_BRASS; + le->leMarkType = LEMT_NONE; +} + +#define EXP_VELOCITY 100 +#define EXP_JUMP 150 +/* +=================== +CG_GibPlayer + +Generated a bunch of gibs launching out from the bodies location +=================== +*/ +void CG_BigExplode( vec3_t playerOrigin ) { + vec3_t origin, velocity; + + if ( !cg_blood.integer ) { + return; + } + + VectorCopy( playerOrigin, origin ); + velocity[0] = crandom()*EXP_VELOCITY; + velocity[1] = crandom()*EXP_VELOCITY; + velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; + CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); + + VectorCopy( playerOrigin, origin ); + velocity[0] = crandom()*EXP_VELOCITY; + velocity[1] = crandom()*EXP_VELOCITY; + velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; + CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); + + VectorCopy( playerOrigin, origin ); + velocity[0] = crandom()*EXP_VELOCITY*1.5; + velocity[1] = crandom()*EXP_VELOCITY*1.5; + velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; + CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); + + VectorCopy( playerOrigin, origin ); + velocity[0] = crandom()*EXP_VELOCITY*2.0; + velocity[1] = crandom()*EXP_VELOCITY*2.0; + velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; + CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); + + VectorCopy( playerOrigin, origin ); + velocity[0] = crandom()*EXP_VELOCITY*2.5; + velocity[1] = crandom()*EXP_VELOCITY*2.5; + velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; + CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); +} + diff --git a/reaction/engine/code/cgame/cg_ents.c b/reaction/engine/code/cgame/cg_ents.c new file mode 100644 index 00000000..a6a5c2a5 --- /dev/null +++ b/reaction/engine/code/cgame/cg_ents.c @@ -0,0 +1,1037 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_ents.c -- present snapshot entities, happens every single frame + +#include "cg_local.h" + + +/* +====================== +CG_PositionEntityOnTag + +Modifies the entities position and axis by the given +tag location +====================== +*/ +void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, + qhandle_t parentModel, char *tagName ) { + int i; + orientation_t lerped; + + // lerp the tag + trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, + 1.0 - parent->backlerp, tagName ); + + // FIXME: allow origin offsets along tag? + VectorCopy( parent->origin, entity->origin ); + for ( i = 0 ; i < 3 ; i++ ) { + VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); + } + + // had to cast away the const to avoid compiler problems... + MatrixMultiply( lerped.axis, ((refEntity_t *)parent)->axis, entity->axis ); + entity->backlerp = parent->backlerp; +} + + +/* +====================== +CG_PositionRotatedEntityOnTag + +Modifies the entities position and axis by the given +tag location +====================== +*/ +void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, + qhandle_t parentModel, char *tagName ) { + int i; + orientation_t lerped; + vec3_t tempAxis[3]; + +//AxisClear( entity->axis ); + // lerp the tag + trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, + 1.0 - parent->backlerp, tagName ); + + // FIXME: allow origin offsets along tag? + VectorCopy( parent->origin, entity->origin ); + for ( i = 0 ; i < 3 ; i++ ) { + VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); + } + + // had to cast away the const to avoid compiler problems... + MatrixMultiply( entity->axis, lerped.axis, tempAxis ); + MatrixMultiply( tempAxis, ((refEntity_t *)parent)->axis, entity->axis ); +} + + + +/* +========================================================================== + +FUNCTIONS CALLED EACH FRAME + +========================================================================== +*/ + +/* +====================== +CG_SetEntitySoundPosition + +Also called by event processing code +====================== +*/ +void CG_SetEntitySoundPosition( centity_t *cent ) { + if ( cent->currentState.solid == SOLID_BMODEL ) { + vec3_t origin; + float *v; + + v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ]; + VectorAdd( cent->lerpOrigin, v, origin ); + trap_S_UpdateEntityPosition( cent->currentState.number, origin ); + } else { + trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin ); + } +} + +/* +================== +CG_EntityEffects + +Add continuous entity effects, like local entity emission and lighting +================== +*/ +static void CG_EntityEffects( centity_t *cent ) { + + // update sound origins + CG_SetEntitySoundPosition( cent ); + + // add loop sound + if ( cent->currentState.loopSound ) { + if (cent->currentState.eType != ET_SPEAKER) { + trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, + cgs.gameSounds[ cent->currentState.loopSound ] ); + } else { + trap_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, + cgs.gameSounds[ cent->currentState.loopSound ] ); + } + } + + + // constant light glow + if ( cent->currentState.constantLight ) { + int cl; + int i, r, g, b; + + cl = cent->currentState.constantLight; + r = cl & 255; + g = ( cl >> 8 ) & 255; + b = ( cl >> 16 ) & 255; + i = ( ( cl >> 24 ) & 255 ) * 4; + trap_R_AddLightToScene( cent->lerpOrigin, i, r, g, b ); + } + +} + + +/* +================== +CG_General +================== +*/ +static void CG_General( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + + s1 = ¢->currentState; + + // if set to invisible, skip + if (!s1->modelindex) { + return; + } + + memset (&ent, 0, sizeof(ent)); + + // set frame + + ent.frame = s1->frame; + ent.oldframe = ent.frame; + ent.backlerp = 0; + + VectorCopy( cent->lerpOrigin, ent.origin); + VectorCopy( cent->lerpOrigin, ent.oldorigin); + + ent.hModel = cgs.gameModels[s1->modelindex]; + + // player model + if (s1->number == cg.snap->ps.clientNum) { + ent.renderfx |= RF_THIRD_PERSON; // only draw from mirrors + } + + // convert angles to axis + AnglesToAxis( cent->lerpAngles, ent.axis ); + + // add to refresh list + trap_R_AddRefEntityToScene (&ent); +} + +/* +================== +CG_Speaker + +Speaker entities can automatically play sounds +================== +*/ +static void CG_Speaker( centity_t *cent ) { + if ( ! cent->currentState.clientNum ) { // FIXME: use something other than clientNum... + return; // not auto triggering + } + + if ( cg.time < cent->miscTime ) { + return; + } + + trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] ); + + // ent->s.frame = ent->wait * 10; + // ent->s.clientNum = ent->random * 10; + cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom(); +} + +/* +================== +CG_Item +================== +*/ +static void CG_Item( centity_t *cent ) { + refEntity_t ent; + entityState_t *es; + gitem_t *item; + int msec; + float frac; + float scale; + weaponInfo_t *wi; + + es = ¢->currentState; + if ( es->modelindex >= bg_numItems ) { + CG_Error( "Bad item index %i on entity", es->modelindex ); + } + + // if set to invisible, skip + if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) ) { + return; + } + + item = &bg_itemlist[ es->modelindex ]; + if ( cg_simpleItems.integer && item->giType != IT_TEAM ) { + memset( &ent, 0, sizeof( ent ) ); + ent.reType = RT_SPRITE; + VectorCopy( cent->lerpOrigin, ent.origin ); + ent.radius = 14; + ent.customShader = cg_items[es->modelindex].icon; + ent.shaderRGBA[0] = 255; + ent.shaderRGBA[1] = 255; + ent.shaderRGBA[2] = 255; + ent.shaderRGBA[3] = 255; + trap_R_AddRefEntityToScene(&ent); + return; + } + + // items bob up and down continuously + scale = 0.005 + cent->currentState.number * 0.00001; + cent->lerpOrigin[2] += 4 + cos( ( cg.time + 1000 ) * scale ) * 4; + + memset (&ent, 0, sizeof(ent)); + + // autorotate at one of two speeds + if ( item->giType == IT_HEALTH ) { + VectorCopy( cg.autoAnglesFast, cent->lerpAngles ); + AxisCopy( cg.autoAxisFast, ent.axis ); + } else { + VectorCopy( cg.autoAngles, cent->lerpAngles ); + AxisCopy( cg.autoAxis, ent.axis ); + } + + wi = NULL; + // the weapons have their origin where they attatch to player + // models, so we need to offset them or they will rotate + // eccentricly + if ( item->giType == IT_WEAPON ) { + wi = &cg_weapons[item->giTag]; + cent->lerpOrigin[0] -= + wi->weaponMidpoint[0] * ent.axis[0][0] + + wi->weaponMidpoint[1] * ent.axis[1][0] + + wi->weaponMidpoint[2] * ent.axis[2][0]; + cent->lerpOrigin[1] -= + wi->weaponMidpoint[0] * ent.axis[0][1] + + wi->weaponMidpoint[1] * ent.axis[1][1] + + wi->weaponMidpoint[2] * ent.axis[2][1]; + cent->lerpOrigin[2] -= + wi->weaponMidpoint[0] * ent.axis[0][2] + + wi->weaponMidpoint[1] * ent.axis[1][2] + + wi->weaponMidpoint[2] * ent.axis[2][2]; + + cent->lerpOrigin[2] += 8; // an extra height boost + } + + ent.hModel = cg_items[es->modelindex].models[0]; + + VectorCopy( cent->lerpOrigin, ent.origin); + VectorCopy( cent->lerpOrigin, ent.oldorigin); + + ent.nonNormalizedAxes = qfalse; + + // if just respawned, slowly scale up + msec = cg.time - cent->miscTime; + if ( msec >= 0 && msec < ITEM_SCALEUP_TIME ) { + frac = (float)msec / ITEM_SCALEUP_TIME; + VectorScale( ent.axis[0], frac, ent.axis[0] ); + VectorScale( ent.axis[1], frac, ent.axis[1] ); + VectorScale( ent.axis[2], frac, ent.axis[2] ); + ent.nonNormalizedAxes = qtrue; + } else { + frac = 1.0; + } + + // items without glow textures need to keep a minimum light value + // so they are always visible + if ( ( item->giType == IT_WEAPON ) || + ( item->giType == IT_ARMOR ) ) { + ent.renderfx |= RF_MINLIGHT; + } + + // increase the size of the weapons when they are presented as items + if ( item->giType == IT_WEAPON ) { + VectorScale( ent.axis[0], 1.5, ent.axis[0] ); + VectorScale( ent.axis[1], 1.5, ent.axis[1] ); + VectorScale( ent.axis[2], 1.5, ent.axis[2] ); + ent.nonNormalizedAxes = qtrue; +#ifdef MISSIONPACK + trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.weaponHoverSound ); +#endif + } + +#ifdef MISSIONPACK + if ( item->giType == IT_HOLDABLE && item->giTag == HI_KAMIKAZE ) { + VectorScale( ent.axis[0], 2, ent.axis[0] ); + VectorScale( ent.axis[1], 2, ent.axis[1] ); + VectorScale( ent.axis[2], 2, ent.axis[2] ); + ent.nonNormalizedAxes = qtrue; + } +#endif + + // add to refresh list + trap_R_AddRefEntityToScene(&ent); + +#ifdef MISSIONPACK + if ( item->giType == IT_WEAPON && wi->barrelModel ) { + refEntity_t barrel; + + memset( &barrel, 0, sizeof( barrel ) ); + + barrel.hModel = wi->barrelModel; + + VectorCopy( ent.lightingOrigin, barrel.lightingOrigin ); + barrel.shadowPlane = ent.shadowPlane; + barrel.renderfx = ent.renderfx; + + CG_PositionRotatedEntityOnTag( &barrel, &ent, wi->weaponModel, "tag_barrel" ); + + AxisCopy( ent.axis, barrel.axis ); + barrel.nonNormalizedAxes = ent.nonNormalizedAxes; + + trap_R_AddRefEntityToScene( &barrel ); + } +#endif + + // accompanying rings / spheres for powerups + if ( !cg_simpleItems.integer ) + { + vec3_t spinAngles; + + VectorClear( spinAngles ); + + if ( item->giType == IT_HEALTH || item->giType == IT_POWERUP ) + { + if ( ( ent.hModel = cg_items[es->modelindex].models[1] ) != 0 ) + { + if ( item->giType == IT_POWERUP ) + { + ent.origin[2] += 12; + spinAngles[1] = ( cg.time & 1023 ) * 360 / -1024.0f; + } + AnglesToAxis( spinAngles, ent.axis ); + + // scale up if respawning + if ( frac != 1.0 ) { + VectorScale( ent.axis[0], frac, ent.axis[0] ); + VectorScale( ent.axis[1], frac, ent.axis[1] ); + VectorScale( ent.axis[2], frac, ent.axis[2] ); + ent.nonNormalizedAxes = qtrue; + } + trap_R_AddRefEntityToScene( &ent ); + } + } + } +} + +//============================================================================ + +/* +=============== +CG_Missile +=============== +*/ +static void CG_Missile( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + const weaponInfo_t *weapon; +// int col; + + s1 = ¢->currentState; + if ( s1->weapon > WP_NUM_WEAPONS ) { + s1->weapon = 0; + } + weapon = &cg_weapons[s1->weapon]; + + // calculate the axis + VectorCopy( s1->angles, cent->lerpAngles); + + // add trails + if ( weapon->missileTrailFunc ) + { + weapon->missileTrailFunc( cent, weapon ); + } +/* + if ( cent->currentState.modelindex == TEAM_RED ) { + col = 1; + } + else if ( cent->currentState.modelindex == TEAM_BLUE ) { + col = 2; + } + else { + col = 0; + } + + // add dynamic light + if ( weapon->missileDlight ) { + trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, + weapon->missileDlightColor[col][0], weapon->missileDlightColor[col][1], weapon->missileDlightColor[col][2] ); + } +*/ + // add dynamic light + if ( weapon->missileDlight ) { + trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, + weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2] ); + } + + // add missile sound + if ( weapon->missileSound ) { + vec3_t velocity; + + BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity ); + + trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound ); + } + + // create the render entity + memset (&ent, 0, sizeof(ent)); + VectorCopy( cent->lerpOrigin, ent.origin); + VectorCopy( cent->lerpOrigin, ent.oldorigin); + + if ( cent->currentState.weapon == WP_PLASMAGUN ) { + ent.reType = RT_SPRITE; + ent.radius = 16; + ent.rotation = 0; + ent.customShader = cgs.media.plasmaBallShader; + trap_R_AddRefEntityToScene( &ent ); + return; + } + + // flicker between two skins + ent.skinNum = cg.clientFrame & 1; + ent.hModel = weapon->missileModel; + ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW; + +#ifdef MISSIONPACK + if ( cent->currentState.weapon == WP_PROX_LAUNCHER ) { + if (s1->generic1 == TEAM_BLUE) { + ent.hModel = cgs.media.blueProxMine; + } + } +#endif + + // convert direction of travel into axis + if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) { + ent.axis[0][2] = 1; + } + + // spin as it moves + if ( s1->pos.trType != TR_STATIONARY ) { + RotateAroundDirection( ent.axis, cg.time / 4 ); + } else { +#ifdef MISSIONPACK + if ( s1->weapon == WP_PROX_LAUNCHER ) { + AnglesToAxis( cent->lerpAngles, ent.axis ); + } + else +#endif + { + RotateAroundDirection( ent.axis, s1->time ); + } + } + + // add to refresh list, possibly with quad glow + CG_AddRefEntityWithPowerups( &ent, s1, TEAM_FREE ); +} + +/* +=============== +CG_Grapple + +This is called when the grapple is sitting up against the wall +=============== +*/ +static void CG_Grapple( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + const weaponInfo_t *weapon; + + s1 = ¢->currentState; + if ( s1->weapon > WP_NUM_WEAPONS ) { + s1->weapon = 0; + } + weapon = &cg_weapons[s1->weapon]; + + // calculate the axis + VectorCopy( s1->angles, cent->lerpAngles); + +#if 0 // FIXME add grapple pull sound here..? + // add missile sound + if ( weapon->missileSound ) { + trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->missileSound ); + } +#endif + + // Will draw cable if needed + CG_GrappleTrail ( cent, weapon ); + + // create the render entity + memset (&ent, 0, sizeof(ent)); + VectorCopy( cent->lerpOrigin, ent.origin); + VectorCopy( cent->lerpOrigin, ent.oldorigin); + + // flicker between two skins + ent.skinNum = cg.clientFrame & 1; + ent.hModel = weapon->missileModel; + ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW; + + // convert direction of travel into axis + if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) { + ent.axis[0][2] = 1; + } + + trap_R_AddRefEntityToScene( &ent ); +} + +/* +=============== +CG_Mover +=============== +*/ +static void CG_Mover( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + + s1 = ¢->currentState; + + // create the render entity + memset (&ent, 0, sizeof(ent)); + VectorCopy( cent->lerpOrigin, ent.origin); + VectorCopy( cent->lerpOrigin, ent.oldorigin); + AnglesToAxis( cent->lerpAngles, ent.axis ); + + ent.renderfx = RF_NOSHADOW; + + // flicker between two skins (FIXME?) + ent.skinNum = ( cg.time >> 6 ) & 1; + + // get the model, either as a bmodel or a modelindex + if ( s1->solid == SOLID_BMODEL ) { + ent.hModel = cgs.inlineDrawModel[s1->modelindex]; + } else { + ent.hModel = cgs.gameModels[s1->modelindex]; + } + + // add to refresh list + trap_R_AddRefEntityToScene(&ent); + + // add the secondary model + if ( s1->modelindex2 ) { + ent.skinNum = 0; + ent.hModel = cgs.gameModels[s1->modelindex2]; + trap_R_AddRefEntityToScene(&ent); + } + +} + +/* +=============== +CG_Beam + +Also called as an event +=============== +*/ +void CG_Beam( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + + s1 = ¢->currentState; + + // create the render entity + memset (&ent, 0, sizeof(ent)); + VectorCopy( s1->pos.trBase, ent.origin ); + VectorCopy( s1->origin2, ent.oldorigin ); + AxisClear( ent.axis ); + ent.reType = RT_BEAM; + + ent.renderfx = RF_NOSHADOW; + + // add to refresh list + trap_R_AddRefEntityToScene(&ent); +} + + +/* +=============== +CG_Portal +=============== +*/ +static void CG_Portal( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + + s1 = ¢->currentState; + + // create the render entity + memset (&ent, 0, sizeof(ent)); + VectorCopy( cent->lerpOrigin, ent.origin ); + VectorCopy( s1->origin2, ent.oldorigin ); + ByteToDir( s1->eventParm, ent.axis[0] ); + PerpendicularVector( ent.axis[1], ent.axis[0] ); + + // negating this tends to get the directions like they want + // we really should have a camera roll value + VectorSubtract( vec3_origin, ent.axis[1], ent.axis[1] ); + + CrossProduct( ent.axis[0], ent.axis[1], ent.axis[2] ); + ent.reType = RT_PORTALSURFACE; + ent.oldframe = s1->powerups; + ent.frame = s1->frame; // rotation speed + ent.skinNum = s1->clientNum/256.0 * 360; // roll offset + + // add to refresh list + trap_R_AddRefEntityToScene(&ent); +} + + +/* +========================= +CG_AdjustPositionForMover + +Also called by client movement prediction code +========================= +*/ +void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ) { + centity_t *cent; + vec3_t oldOrigin, origin, deltaOrigin; + vec3_t oldAngles, angles, deltaAngles; + + if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) { + VectorCopy( in, out ); + return; + } + + cent = &cg_entities[ moverNum ]; + if ( cent->currentState.eType != ET_MOVER ) { + VectorCopy( in, out ); + return; + } + + BG_EvaluateTrajectory( ¢->currentState.pos, fromTime, oldOrigin ); + BG_EvaluateTrajectory( ¢->currentState.apos, fromTime, oldAngles ); + + BG_EvaluateTrajectory( ¢->currentState.pos, toTime, origin ); + BG_EvaluateTrajectory( ¢->currentState.apos, toTime, angles ); + + VectorSubtract( origin, oldOrigin, deltaOrigin ); + VectorSubtract( angles, oldAngles, deltaAngles ); + + VectorAdd( in, deltaOrigin, out ); + + // FIXME: origin change when on a rotating object +} + + +/* +============================= +CG_InterpolateEntityPosition +============================= +*/ +static void CG_InterpolateEntityPosition( centity_t *cent ) { + vec3_t current, next; + float f; + + // it would be an internal error to find an entity that interpolates without + // a snapshot ahead of the current one + if ( cg.nextSnap == NULL ) { + CG_Error( "CG_InterpoateEntityPosition: cg.nextSnap == NULL" ); + } + + f = cg.frameInterpolation; + + // this will linearize a sine or parabolic curve, but it is important + // to not extrapolate player positions if more recent data is available + BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, current ); + BG_EvaluateTrajectory( ¢->nextState.pos, cg.nextSnap->serverTime, next ); + + cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] ); + cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] ); + cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] ); + + BG_EvaluateTrajectory( ¢->currentState.apos, cg.snap->serverTime, current ); + BG_EvaluateTrajectory( ¢->nextState.apos, cg.nextSnap->serverTime, next ); + + cent->lerpAngles[0] = LerpAngle( current[0], next[0], f ); + cent->lerpAngles[1] = LerpAngle( current[1], next[1], f ); + cent->lerpAngles[2] = LerpAngle( current[2], next[2], f ); + +} + +/* +=============== +CG_CalcEntityLerpPositions + +=============== +*/ +static void CG_CalcEntityLerpPositions( centity_t *cent ) { + + // if this player does not want to see extrapolated players + if ( !cg_smoothClients.integer ) { + // make sure the clients use TR_INTERPOLATE + if ( cent->currentState.number < MAX_CLIENTS ) { + cent->currentState.pos.trType = TR_INTERPOLATE; + cent->nextState.pos.trType = TR_INTERPOLATE; + } + } + + if ( cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) { + CG_InterpolateEntityPosition( cent ); + return; + } + + // first see if we can interpolate between two snaps for + // linear extrapolated clients + if ( cent->interpolate && cent->currentState.pos.trType == TR_LINEAR_STOP && + cent->currentState.number < MAX_CLIENTS) { + CG_InterpolateEntityPosition( cent ); + return; + } + + // just use the current frame and evaluate as best we can + BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin ); + BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles ); + + // adjust for riding a mover if it wasn't rolled into the predicted + // player state + if ( cent != &cg.predictedPlayerEntity ) { + CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum, + cg.snap->serverTime, cg.time, cent->lerpOrigin ); + } +} + +/* +=============== +CG_TeamBase +=============== +*/ +static void CG_TeamBase( centity_t *cent ) { + refEntity_t model; +#ifdef MISSIONPACK + vec3_t angles; + int t, h; + float c; + + if ( cgs.gametype == GT_CTF || cgs.gametype == GT_1FCTF ) { +#else + if ( cgs.gametype == GT_CTF) { +#endif + // show the flag base + memset(&model, 0, sizeof(model)); + model.reType = RT_MODEL; + VectorCopy( cent->lerpOrigin, model.lightingOrigin ); + VectorCopy( cent->lerpOrigin, model.origin ); + AnglesToAxis( cent->currentState.angles, model.axis ); + if ( cent->currentState.modelindex == TEAM_RED ) { + model.hModel = cgs.media.redFlagBaseModel; + } + else if ( cent->currentState.modelindex == TEAM_BLUE ) { + model.hModel = cgs.media.blueFlagBaseModel; + } + else { + model.hModel = cgs.media.neutralFlagBaseModel; + } + trap_R_AddRefEntityToScene( &model ); + } +#ifdef MISSIONPACK + else if ( cgs.gametype == GT_OBELISK ) { + // show the obelisk + memset(&model, 0, sizeof(model)); + model.reType = RT_MODEL; + VectorCopy( cent->lerpOrigin, model.lightingOrigin ); + VectorCopy( cent->lerpOrigin, model.origin ); + AnglesToAxis( cent->currentState.angles, model.axis ); + + model.hModel = cgs.media.overloadBaseModel; + trap_R_AddRefEntityToScene( &model ); + // if hit + if ( cent->currentState.frame == 1) { + // show hit model + // modelindex2 is the health value of the obelisk + c = cent->currentState.modelindex2; + model.shaderRGBA[0] = 0xff; + model.shaderRGBA[1] = c; + model.shaderRGBA[2] = c; + model.shaderRGBA[3] = 0xff; + // + model.hModel = cgs.media.overloadEnergyModel; + trap_R_AddRefEntityToScene( &model ); + } + // if respawning + if ( cent->currentState.frame == 2) { + if ( !cent->miscTime ) { + cent->miscTime = cg.time; + } + t = cg.time - cent->miscTime; + h = (cg_obeliskRespawnDelay.integer - 5) * 1000; + // + if (t > h) { + c = (float) (t - h) / h; + if (c > 1) + c = 1; + } + else { + c = 0; + } + // show the lights + AnglesToAxis( cent->currentState.angles, model.axis ); + // + model.shaderRGBA[0] = c * 0xff; + model.shaderRGBA[1] = c * 0xff; + model.shaderRGBA[2] = c * 0xff; + model.shaderRGBA[3] = c * 0xff; + + model.hModel = cgs.media.overloadLightsModel; + trap_R_AddRefEntityToScene( &model ); + // show the target + if (t > h) { + if ( !cent->muzzleFlashTime ) { + trap_S_StartSound (cent->lerpOrigin, ENTITYNUM_NONE, CHAN_BODY, cgs.media.obeliskRespawnSound); + cent->muzzleFlashTime = 1; + } + VectorCopy(cent->currentState.angles, angles); + angles[YAW] += (float) 16 * acos(1-c) * 180 / M_PI; + AnglesToAxis( angles, model.axis ); + + VectorScale( model.axis[0], c, model.axis[0]); + VectorScale( model.axis[1], c, model.axis[1]); + VectorScale( model.axis[2], c, model.axis[2]); + + model.shaderRGBA[0] = 0xff; + model.shaderRGBA[1] = 0xff; + model.shaderRGBA[2] = 0xff; + model.shaderRGBA[3] = 0xff; + // + model.origin[2] += 56; + model.hModel = cgs.media.overloadTargetModel; + trap_R_AddRefEntityToScene( &model ); + } + else { + //FIXME: show animated smoke + } + } + else { + cent->miscTime = 0; + cent->muzzleFlashTime = 0; + // modelindex2 is the health value of the obelisk + c = cent->currentState.modelindex2; + model.shaderRGBA[0] = 0xff; + model.shaderRGBA[1] = c; + model.shaderRGBA[2] = c; + model.shaderRGBA[3] = 0xff; + // show the lights + model.hModel = cgs.media.overloadLightsModel; + trap_R_AddRefEntityToScene( &model ); + // show the target + model.origin[2] += 56; + model.hModel = cgs.media.overloadTargetModel; + trap_R_AddRefEntityToScene( &model ); + } + } + else if ( cgs.gametype == GT_HARVESTER ) { + // show harvester model + memset(&model, 0, sizeof(model)); + model.reType = RT_MODEL; + VectorCopy( cent->lerpOrigin, model.lightingOrigin ); + VectorCopy( cent->lerpOrigin, model.origin ); + AnglesToAxis( cent->currentState.angles, model.axis ); + + if ( cent->currentState.modelindex == TEAM_RED ) { + model.hModel = cgs.media.harvesterModel; + model.customSkin = cgs.media.harvesterRedSkin; + } + else if ( cent->currentState.modelindex == TEAM_BLUE ) { + model.hModel = cgs.media.harvesterModel; + model.customSkin = cgs.media.harvesterBlueSkin; + } + else { + model.hModel = cgs.media.harvesterNeutralModel; + model.customSkin = 0; + } + trap_R_AddRefEntityToScene( &model ); + } +#endif +} + +/* +=============== +CG_AddCEntity + +=============== +*/ +static void CG_AddCEntity( centity_t *cent ) { + // event-only entities will have been dealt with already + if ( cent->currentState.eType >= ET_EVENTS ) { + return; + } + + // calculate the current origin + CG_CalcEntityLerpPositions( cent ); + + // add automatic effects + CG_EntityEffects( cent ); + + switch ( cent->currentState.eType ) { + default: + CG_Error( "Bad entity type: %i\n", cent->currentState.eType ); + break; + case ET_INVISIBLE: + case ET_PUSH_TRIGGER: + case ET_TELEPORT_TRIGGER: + break; + case ET_GENERAL: + CG_General( cent ); + break; + case ET_PLAYER: + CG_Player( cent ); + break; + case ET_ITEM: + CG_Item( cent ); + break; + case ET_MISSILE: + CG_Missile( cent ); + break; + case ET_MOVER: + CG_Mover( cent ); + break; + case ET_BEAM: + CG_Beam( cent ); + break; + case ET_PORTAL: + CG_Portal( cent ); + break; + case ET_SPEAKER: + CG_Speaker( cent ); + break; + case ET_GRAPPLE: + CG_Grapple( cent ); + break; + case ET_TEAM: + CG_TeamBase( cent ); + break; + } +} + +/* +=============== +CG_AddPacketEntities + +=============== +*/ +void CG_AddPacketEntities( void ) { + int num; + centity_t *cent; + playerState_t *ps; + + // set cg.frameInterpolation + if ( cg.nextSnap ) { + int delta; + + delta = (cg.nextSnap->serverTime - cg.snap->serverTime); + if ( delta == 0 ) { + cg.frameInterpolation = 0; + } else { + cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta; + } + } else { + cg.frameInterpolation = 0; // actually, it should never be used, because + // no entities should be marked as interpolating + } + + // the auto-rotating items will all have the same axis + cg.autoAngles[0] = 0; + cg.autoAngles[1] = ( cg.time & 2047 ) * 360 / 2048.0; + cg.autoAngles[2] = 0; + + cg.autoAnglesFast[0] = 0; + cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f; + cg.autoAnglesFast[2] = 0; + + AnglesToAxis( cg.autoAngles, cg.autoAxis ); + AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast ); + + // generate and add the entity from the playerstate + ps = &cg.predictedPlayerState; + BG_PlayerStateToEntityState( ps, &cg.predictedPlayerEntity.currentState, qfalse ); + CG_AddCEntity( &cg.predictedPlayerEntity ); + + // lerp the non-predicted value for lightning gun origins + CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] ); + + // add each entity sent over by the server + for ( num = 0 ; num < cg.snap->numEntities ; num++ ) { + cent = &cg_entities[ cg.snap->entities[ num ].number ]; + CG_AddCEntity( cent ); + } +} + diff --git a/reaction/engine/code/cgame/cg_event.c b/reaction/engine/code/cgame/cg_event.c new file mode 100644 index 00000000..7467cb62 --- /dev/null +++ b/reaction/engine/code/cgame/cg_event.c @@ -0,0 +1,1205 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_event.c -- handle entity events at snapshot or playerstate transitions + +#include "cg_local.h" + +// for the voice chats +#ifdef MISSIONPACK +#include "../../ui/menudef.h" +#endif +//========================================================================== + +/* +=================== +CG_PlaceString + +Also called by scoreboard drawing +=================== +*/ +const char *CG_PlaceString( int rank ) { + static char str[64]; + char *s, *t; + + if ( rank & RANK_TIED_FLAG ) { + rank &= ~RANK_TIED_FLAG; + t = "Tied for "; + } else { + t = ""; + } + + if ( rank == 1 ) { + s = S_COLOR_BLUE "1st" S_COLOR_WHITE; // draw in blue + } else if ( rank == 2 ) { + s = S_COLOR_RED "2nd" S_COLOR_WHITE; // draw in red + } else if ( rank == 3 ) { + s = S_COLOR_YELLOW "3rd" S_COLOR_WHITE; // draw in yellow + } else if ( rank == 11 ) { + s = "11th"; + } else if ( rank == 12 ) { + s = "12th"; + } else if ( rank == 13 ) { + s = "13th"; + } else if ( rank % 10 == 1 ) { + s = va("%ist", rank); + } else if ( rank % 10 == 2 ) { + s = va("%ind", rank); + } else if ( rank % 10 == 3 ) { + s = va("%ird", rank); + } else { + s = va("%ith", rank); + } + + Com_sprintf( str, sizeof( str ), "%s%s", t, s ); + return str; +} + +/* +============= +CG_Obituary +============= +*/ +static void CG_Obituary( entityState_t *ent ) { + int mod; + int target, attacker; + char *message; + char *message2; + const char *targetInfo; + const char *attackerInfo; + char targetName[32]; + char attackerName[32]; + gender_t gender; + clientInfo_t *ci; + + target = ent->otherEntityNum; + attacker = ent->otherEntityNum2; + mod = ent->eventParm; + + if ( target < 0 || target >= MAX_CLIENTS ) { + CG_Error( "CG_Obituary: target out of range" ); + } + ci = &cgs.clientinfo[target]; + + if ( attacker < 0 || attacker >= MAX_CLIENTS ) { + attacker = ENTITYNUM_WORLD; + attackerInfo = NULL; + } else { + attackerInfo = CG_ConfigString( CS_PLAYERS + attacker ); + } + + targetInfo = CG_ConfigString( CS_PLAYERS + target ); + if ( !targetInfo ) { + return; + } + Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof(targetName) - 2); + strcat( targetName, S_COLOR_WHITE ); + + message2 = ""; + + // check for single client messages + + switch( mod ) { + case MOD_SUICIDE: + message = "suicides"; + break; + case MOD_FALLING: + message = "cratered"; + break; + case MOD_CRUSH: + message = "was squished"; + break; + case MOD_WATER: + message = "sank like a rock"; + break; + case MOD_SLIME: + message = "melted"; + break; + case MOD_LAVA: + message = "does a back flip into the lava"; + break; + case MOD_TARGET_LASER: + message = "saw the light"; + break; + case MOD_TRIGGER_HURT: + message = "was in the wrong place"; + break; + default: + message = NULL; + break; + } + + if (attacker == target) { + gender = ci->gender; + switch (mod) { +#ifdef MISSIONPACK + case MOD_KAMIKAZE: + message = "goes out with a bang"; + break; +#endif + case MOD_GRENADE_SPLASH: + if ( gender == GENDER_FEMALE ) + message = "tripped on her own grenade"; + else if ( gender == GENDER_NEUTER ) + message = "tripped on its own grenade"; + else + message = "tripped on his own grenade"; + break; + case MOD_ROCKET_SPLASH: + if ( gender == GENDER_FEMALE ) + message = "blew herself up"; + else if ( gender == GENDER_NEUTER ) + message = "blew itself up"; + else + message = "blew himself up"; + break; + case MOD_PLASMA_SPLASH: + if ( gender == GENDER_FEMALE ) + message = "melted herself"; + else if ( gender == GENDER_NEUTER ) + message = "melted itself"; + else + message = "melted himself"; + break; + case MOD_BFG_SPLASH: + message = "should have used a smaller gun"; + break; +#ifdef MISSIONPACK + case MOD_PROXIMITY_MINE: + if( gender == GENDER_FEMALE ) { + message = "found her prox mine"; + } else if ( gender == GENDER_NEUTER ) { + message = "found it's prox mine"; + } else { + message = "found his prox mine"; + } + break; +#endif + default: + if ( gender == GENDER_FEMALE ) + message = "killed herself"; + else if ( gender == GENDER_NEUTER ) + message = "killed itself"; + else + message = "killed himself"; + break; + } + } + + if (message) { + CG_Printf( "%s %s.\n", targetName, message); + return; + } + + // check for kill messages from the current clientNum + if ( attacker == cg.snap->ps.clientNum ) { + char *s; + + if ( cgs.gametype < GT_TEAM ) { + s = va("You fragged %s\n%s place with %i", targetName, + CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ), + cg.snap->ps.persistant[PERS_SCORE] ); + } else { + s = va("You fragged %s", targetName ); + } +#ifdef MISSIONPACK + if (!(cg_singlePlayerActive.integer && cg_cameraOrbit.integer)) { + CG_CenterPrint( s, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); + } +#else + CG_CenterPrint( s, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); +#endif + + // print the text message as well + } + + // check for double client messages + if ( !attackerInfo ) { + attacker = ENTITYNUM_WORLD; + strcpy( attackerName, "noname" ); + } else { + Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof(attackerName) - 2); + strcat( attackerName, S_COLOR_WHITE ); + // check for kill messages about the current clientNum + if ( target == cg.snap->ps.clientNum ) { + Q_strncpyz( cg.killerName, attackerName, sizeof( cg.killerName ) ); + } + } + + if ( attacker != ENTITYNUM_WORLD ) { + switch (mod) { + case MOD_GRAPPLE: + message = "was caught by"; + break; + case MOD_GAUNTLET: + message = "was pummeled by"; + break; + case MOD_MACHINEGUN: + message = "was machinegunned by"; + break; + case MOD_SHOTGUN: + message = "was gunned down by"; + break; + case MOD_GRENADE: + message = "ate"; + message2 = "'s grenade"; + break; + case MOD_GRENADE_SPLASH: + message = "was shredded by"; + message2 = "'s shrapnel"; + break; + case MOD_ROCKET: + message = "ate"; + message2 = "'s rocket"; + break; + case MOD_ROCKET_SPLASH: + message = "almost dodged"; + message2 = "'s rocket"; + break; + case MOD_PLASMA: + message = "was melted by"; + message2 = "'s plasmagun"; + break; + case MOD_PLASMA_SPLASH: + message = "was melted by"; + message2 = "'s plasmagun"; + break; + case MOD_RAILGUN: + message = "was railed by"; + break; + case MOD_LIGHTNING: + message = "was electrocuted by"; + break; + case MOD_BFG: + case MOD_BFG_SPLASH: + message = "was blasted by"; + message2 = "'s BFG"; + break; +#ifdef MISSIONPACK + case MOD_NAIL: + message = "was nailed by"; + break; + case MOD_CHAINGUN: + message = "got lead poisoning from"; + message2 = "'s Chaingun"; + break; + case MOD_PROXIMITY_MINE: + message = "was too close to"; + message2 = "'s Prox Mine"; + break; + case MOD_KAMIKAZE: + message = "falls to"; + message2 = "'s Kamikaze blast"; + break; + case MOD_JUICED: + message = "was juiced by"; + break; +#endif + case MOD_TELEFRAG: + message = "tried to invade"; + message2 = "'s personal space"; + break; + default: + message = "was killed by"; + break; + } + + if (message) { + CG_Printf( "%s %s %s%s\n", + targetName, message, attackerName, message2); + return; + } + } + + // we don't know what it was + CG_Printf( "%s died.\n", targetName ); +} + +//========================================================================== + +/* +=============== +CG_UseItem +=============== +*/ +static void CG_UseItem( centity_t *cent ) { + clientInfo_t *ci; + int itemNum, clientNum; + gitem_t *item; + entityState_t *es; + + es = ¢->currentState; + + itemNum = (es->event & ~EV_EVENT_BITS) - EV_USE_ITEM0; + if ( itemNum < 0 || itemNum > HI_NUM_HOLDABLE ) { + itemNum = 0; + } + + // print a message if the local player + if ( es->number == cg.snap->ps.clientNum ) { + if ( !itemNum ) { + CG_CenterPrint( "No item to use", SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); + } else { + item = BG_FindItemForHoldable( itemNum ); + CG_CenterPrint( va("Use %s", item->pickup_name), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); + } + } + + switch ( itemNum ) { + default: + case HI_NONE: + trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.useNothingSound ); + break; + + case HI_TELEPORTER: + break; + + case HI_MEDKIT: + clientNum = cent->currentState.clientNum; + if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) { + ci = &cgs.clientinfo[ clientNum ]; + ci->medkitUsageTime = cg.time; + } + trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.medkitSound ); + break; + +#ifdef MISSIONPACK + case HI_KAMIKAZE: + break; + + case HI_PORTAL: + break; + case HI_INVULNERABILITY: + trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.useInvulnerabilitySound ); + break; +#endif + } + +} + +/* +================ +CG_ItemPickup + +A new item was picked up this frame +================ +*/ +static void CG_ItemPickup( int itemNum ) { + cg.itemPickup = itemNum; + cg.itemPickupTime = cg.time; + cg.itemPickupBlendTime = cg.time; + // see if it should be the grabbed weapon + if ( bg_itemlist[itemNum].giType == IT_WEAPON ) { + // select it immediately + if ( cg_autoswitch.integer && bg_itemlist[itemNum].giTag != WP_MACHINEGUN ) { + cg.weaponSelectTime = cg.time; + cg.weaponSelect = bg_itemlist[itemNum].giTag; + } + } + +} + + +/* +================ +CG_PainEvent + +Also called by playerstate transition +================ +*/ +void CG_PainEvent( centity_t *cent, int health ) { + char *snd; + + // don't do more than two pain sounds a second + if ( cg.time - cent->pe.painTime < 500 ) { + return; + } + + if ( health < 25 ) { + snd = "*pain25_1.wav"; + } else if ( health < 50 ) { + snd = "*pain50_1.wav"; + } else if ( health < 75 ) { + snd = "*pain75_1.wav"; + } else { + snd = "*pain100_1.wav"; + } + trap_S_StartSound( NULL, cent->currentState.number, CHAN_VOICE, + CG_CustomSound( cent->currentState.number, snd ) ); + + // save pain time for programitic twitch animation + cent->pe.painTime = cg.time; + cent->pe.painDirection ^= 1; +} + + + +/* +============== +CG_EntityEvent + +An entity has an event value +also called by CG_CheckPlayerstateEvents +============== +*/ +#define DEBUGNAME(x) if(cg_debugEvents.integer){CG_Printf(x"\n");} +void CG_EntityEvent( centity_t *cent, vec3_t position ) { + entityState_t *es; + int event; + vec3_t dir; + const char *s; + int clientNum; + clientInfo_t *ci; + + es = ¢->currentState; + event = es->event & ~EV_EVENT_BITS; + + if ( cg_debugEvents.integer ) { + CG_Printf( "ent:%3i event:%3i ", es->number, event ); + } + + if ( !event ) { + DEBUGNAME("ZEROEVENT"); + return; + } + + clientNum = es->clientNum; + if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { + clientNum = 0; + } + ci = &cgs.clientinfo[ clientNum ]; + + switch ( event ) { + // + // movement generated events + // + case EV_FOOTSTEP: + DEBUGNAME("EV_FOOTSTEP"); + if (cg_footsteps.integer) { + trap_S_StartSound (NULL, es->number, CHAN_BODY, + cgs.media.footsteps[ ci->footsteps ][rand()&3] ); + } + break; + case EV_FOOTSTEP_METAL: + DEBUGNAME("EV_FOOTSTEP_METAL"); + if (cg_footsteps.integer) { + trap_S_StartSound (NULL, es->number, CHAN_BODY, + cgs.media.footsteps[ FOOTSTEP_METAL ][rand()&3] ); + } + break; + case EV_FOOTSPLASH: + DEBUGNAME("EV_FOOTSPLASH"); + if (cg_footsteps.integer) { + trap_S_StartSound (NULL, es->number, CHAN_BODY, + cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] ); + } + break; + case EV_FOOTWADE: + DEBUGNAME("EV_FOOTWADE"); + if (cg_footsteps.integer) { + trap_S_StartSound (NULL, es->number, CHAN_BODY, + cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] ); + } + break; + case EV_SWIM: + DEBUGNAME("EV_SWIM"); + if (cg_footsteps.integer) { + trap_S_StartSound (NULL, es->number, CHAN_BODY, + cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] ); + } + break; + + + case EV_FALL_SHORT: + DEBUGNAME("EV_FALL_SHORT"); + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.landSound ); + if ( clientNum == cg.predictedPlayerState.clientNum ) { + // smooth landing z changes + cg.landChange = -8; + cg.landTime = cg.time; + } + break; + case EV_FALL_MEDIUM: + DEBUGNAME("EV_FALL_MEDIUM"); + // use normal pain sound + trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*pain100_1.wav" ) ); + if ( clientNum == cg.predictedPlayerState.clientNum ) { + // smooth landing z changes + cg.landChange = -16; + cg.landTime = cg.time; + } + break; + case EV_FALL_FAR: + DEBUGNAME("EV_FALL_FAR"); + trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*fall1.wav" ) ); + cent->pe.painTime = cg.time; // don't play a pain sound right after this + if ( clientNum == cg.predictedPlayerState.clientNum ) { + // smooth landing z changes + cg.landChange = -24; + cg.landTime = cg.time; + } + break; + + case EV_STEP_4: + case EV_STEP_8: + case EV_STEP_12: + case EV_STEP_16: // smooth out step up transitions + DEBUGNAME("EV_STEP"); + { + float oldStep; + int delta; + int step; + + if ( clientNum != cg.predictedPlayerState.clientNum ) { + break; + } + // if we are interpolating, we don't need to smooth steps + if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) || + cg_nopredict.integer || cg_synchronousClients.integer ) { + break; + } + // check for stepping up before a previous step is completed + delta = cg.time - cg.stepTime; + if (delta < STEP_TIME) { + oldStep = cg.stepChange * (STEP_TIME - delta) / STEP_TIME; + } else { + oldStep = 0; + } + + // add this amount + step = 4 * (event - EV_STEP_4 + 1 ); + cg.stepChange = oldStep + step; + if ( cg.stepChange > MAX_STEP_CHANGE ) { + cg.stepChange = MAX_STEP_CHANGE; + } + cg.stepTime = cg.time; + break; + } + + case EV_JUMP_PAD: + DEBUGNAME("EV_JUMP_PAD"); +// CG_Printf( "EV_JUMP_PAD w/effect #%i\n", es->eventParm ); + { + localEntity_t *smoke; + vec3_t up = {0, 0, 1}; + + + smoke = CG_SmokePuff( cent->lerpOrigin, up, + 32, + 1, 1, 1, 0.33f, + 1000, + cg.time, 0, + LEF_PUFF_DONT_SCALE, + cgs.media.smokePuffShader ); + } + + // boing sound at origin, jump sound on player + trap_S_StartSound ( cent->lerpOrigin, -1, CHAN_VOICE, cgs.media.jumpPadSound ); + trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) ); + break; + + case EV_JUMP: + DEBUGNAME("EV_JUMP"); + trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) ); + break; + case EV_TAUNT: + DEBUGNAME("EV_TAUNT"); + trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*taunt.wav" ) ); + break; +#ifdef MISSIONPACK + case EV_TAUNT_YES: + DEBUGNAME("EV_TAUNT_YES"); + CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_YES); + break; + case EV_TAUNT_NO: + DEBUGNAME("EV_TAUNT_NO"); + CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_NO); + break; + case EV_TAUNT_FOLLOWME: + DEBUGNAME("EV_TAUNT_FOLLOWME"); + CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_FOLLOWME); + break; + case EV_TAUNT_GETFLAG: + DEBUGNAME("EV_TAUNT_GETFLAG"); + CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_ONGETFLAG); + break; + case EV_TAUNT_GUARDBASE: + DEBUGNAME("EV_TAUNT_GUARDBASE"); + CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_ONDEFENSE); + break; + case EV_TAUNT_PATROL: + DEBUGNAME("EV_TAUNT_PATROL"); + CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_ONPATROL); + break; +#endif + case EV_WATER_TOUCH: + DEBUGNAME("EV_WATER_TOUCH"); + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrInSound ); + break; + case EV_WATER_LEAVE: + DEBUGNAME("EV_WATER_LEAVE"); + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrOutSound ); + break; + case EV_WATER_UNDER: + DEBUGNAME("EV_WATER_UNDER"); + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrUnSound ); + break; + case EV_WATER_CLEAR: + DEBUGNAME("EV_WATER_CLEAR"); + trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*gasp.wav" ) ); + break; + + case EV_ITEM_PICKUP: + DEBUGNAME("EV_ITEM_PICKUP"); + { + gitem_t *item; + int index; + + index = es->eventParm; // player predicted + + if ( index < 1 || index >= bg_numItems ) { + break; + } + item = &bg_itemlist[ index ]; + + // powerups and team items will have a separate global sound, this one + // will be played at prediction time + if ( item->giType == IT_POWERUP || item->giType == IT_TEAM) { + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.n_healthSound ); + } else if (item->giType == IT_PERSISTANT_POWERUP) { +#ifdef MISSIONPACK + switch (item->giTag ) { + case PW_SCOUT: + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.scoutSound ); + break; + case PW_GUARD: + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.guardSound ); + break; + case PW_DOUBLER: + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.doublerSound ); + break; + case PW_AMMOREGEN: + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.ammoregenSound ); + break; + } +#endif + } else { + trap_S_StartSound (NULL, es->number, CHAN_AUTO, trap_S_RegisterSound( item->pickup_sound, qfalse ) ); + } + + // show icon and name on status bar + if ( es->number == cg.snap->ps.clientNum ) { + CG_ItemPickup( index ); + } + } + break; + + case EV_GLOBAL_ITEM_PICKUP: + DEBUGNAME("EV_GLOBAL_ITEM_PICKUP"); + { + gitem_t *item; + int index; + + index = es->eventParm; // player predicted + + if ( index < 1 || index >= bg_numItems ) { + break; + } + item = &bg_itemlist[ index ]; + // powerup pickups are global + if( item->pickup_sound ) { + trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( item->pickup_sound, qfalse ) ); + } + + // show icon and name on status bar + if ( es->number == cg.snap->ps.clientNum ) { + CG_ItemPickup( index ); + } + } + break; + + // + // weapon events + // + case EV_NOAMMO: + DEBUGNAME("EV_NOAMMO"); +// trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.noAmmoSound ); + if ( es->number == cg.snap->ps.clientNum ) { + CG_OutOfAmmoChange(); + } + break; + case EV_CHANGE_WEAPON: + DEBUGNAME("EV_CHANGE_WEAPON"); + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.selectSound ); + break; + case EV_FIRE_WEAPON: + DEBUGNAME("EV_FIRE_WEAPON"); + CG_FireWeapon( cent ); + break; + + case EV_USE_ITEM0: + DEBUGNAME("EV_USE_ITEM0"); + CG_UseItem( cent ); + break; + case EV_USE_ITEM1: + DEBUGNAME("EV_USE_ITEM1"); + CG_UseItem( cent ); + break; + case EV_USE_ITEM2: + DEBUGNAME("EV_USE_ITEM2"); + CG_UseItem( cent ); + break; + case EV_USE_ITEM3: + DEBUGNAME("EV_USE_ITEM3"); + CG_UseItem( cent ); + break; + case EV_USE_ITEM4: + DEBUGNAME("EV_USE_ITEM4"); + CG_UseItem( cent ); + break; + case EV_USE_ITEM5: + DEBUGNAME("EV_USE_ITEM5"); + CG_UseItem( cent ); + break; + case EV_USE_ITEM6: + DEBUGNAME("EV_USE_ITEM6"); + CG_UseItem( cent ); + break; + case EV_USE_ITEM7: + DEBUGNAME("EV_USE_ITEM7"); + CG_UseItem( cent ); + break; + case EV_USE_ITEM8: + DEBUGNAME("EV_USE_ITEM8"); + CG_UseItem( cent ); + break; + case EV_USE_ITEM9: + DEBUGNAME("EV_USE_ITEM9"); + CG_UseItem( cent ); + break; + case EV_USE_ITEM10: + DEBUGNAME("EV_USE_ITEM10"); + CG_UseItem( cent ); + break; + case EV_USE_ITEM11: + DEBUGNAME("EV_USE_ITEM11"); + CG_UseItem( cent ); + break; + case EV_USE_ITEM12: + DEBUGNAME("EV_USE_ITEM12"); + CG_UseItem( cent ); + break; + case EV_USE_ITEM13: + DEBUGNAME("EV_USE_ITEM13"); + CG_UseItem( cent ); + break; + case EV_USE_ITEM14: + DEBUGNAME("EV_USE_ITEM14"); + CG_UseItem( cent ); + break; + + //================================================================= + + // + // other events + // + case EV_PLAYER_TELEPORT_IN: + DEBUGNAME("EV_PLAYER_TELEPORT_IN"); + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleInSound ); + CG_SpawnEffect( position); + break; + + case EV_PLAYER_TELEPORT_OUT: + DEBUGNAME("EV_PLAYER_TELEPORT_OUT"); + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleOutSound ); + CG_SpawnEffect( position); + break; + + case EV_ITEM_POP: + DEBUGNAME("EV_ITEM_POP"); + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound ); + break; + case EV_ITEM_RESPAWN: + DEBUGNAME("EV_ITEM_RESPAWN"); + cent->miscTime = cg.time; // scale up from this + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound ); + break; + + case EV_GRENADE_BOUNCE: + DEBUGNAME("EV_GRENADE_BOUNCE"); + if ( rand() & 1 ) { + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.hgrenb1aSound ); + } else { + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.hgrenb2aSound ); + } + break; + +#ifdef MISSIONPACK + case EV_PROXIMITY_MINE_STICK: + DEBUGNAME("EV_PROXIMITY_MINE_STICK"); + if( es->eventParm & SURF_FLESH ) { + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbimplSound ); + } else if( es->eventParm & SURF_METALSTEPS ) { + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbimpmSound ); + } else { + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbimpdSound ); + } + break; + + case EV_PROXIMITY_MINE_TRIGGER: + DEBUGNAME("EV_PROXIMITY_MINE_TRIGGER"); + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbactvSound ); + break; + case EV_KAMIKAZE: + DEBUGNAME("EV_KAMIKAZE"); + CG_KamikazeEffect( cent->lerpOrigin ); + break; + case EV_OBELISKEXPLODE: + DEBUGNAME("EV_OBELISKEXPLODE"); + CG_ObeliskExplode( cent->lerpOrigin, es->eventParm ); + break; + case EV_OBELISKPAIN: + DEBUGNAME("EV_OBELISKPAIN"); + CG_ObeliskPain( cent->lerpOrigin ); + break; + case EV_INVUL_IMPACT: + DEBUGNAME("EV_INVUL_IMPACT"); + CG_InvulnerabilityImpact( cent->lerpOrigin, cent->currentState.angles ); + break; + case EV_JUICED: + DEBUGNAME("EV_JUICED"); + CG_InvulnerabilityJuiced( cent->lerpOrigin ); + break; + case EV_LIGHTNINGBOLT: + DEBUGNAME("EV_LIGHTNINGBOLT"); + CG_LightningBoltBeam(es->origin2, es->pos.trBase); + break; +#endif + case EV_SCOREPLUM: + DEBUGNAME("EV_SCOREPLUM"); + CG_ScorePlum( cent->currentState.otherEntityNum, cent->lerpOrigin, cent->currentState.time ); + break; + + // + // missile impacts + // + case EV_MISSILE_HIT: + DEBUGNAME("EV_MISSILE_HIT"); + ByteToDir( es->eventParm, dir ); + CG_MissileHitPlayer( es->weapon, position, dir, es->otherEntityNum ); + break; + + case EV_MISSILE_MISS: + DEBUGNAME("EV_MISSILE_MISS"); + ByteToDir( es->eventParm, dir ); + CG_MissileHitWall( es->weapon, 0, position, dir, IMPACTSOUND_DEFAULT ); + break; + + case EV_MISSILE_MISS_METAL: + DEBUGNAME("EV_MISSILE_MISS_METAL"); + ByteToDir( es->eventParm, dir ); + CG_MissileHitWall( es->weapon, 0, position, dir, IMPACTSOUND_METAL ); + break; + + case EV_RAILTRAIL: + DEBUGNAME("EV_RAILTRAIL"); + cent->currentState.weapon = WP_RAILGUN; + // if the end was on a nomark surface, don't make an explosion + CG_RailTrail( ci, es->origin2, es->pos.trBase ); + if ( es->eventParm != 255 ) { + ByteToDir( es->eventParm, dir ); + CG_MissileHitWall( es->weapon, es->clientNum, position, dir, IMPACTSOUND_DEFAULT ); + } + break; + + case EV_BULLET_HIT_WALL: + DEBUGNAME("EV_BULLET_HIT_WALL"); + ByteToDir( es->eventParm, dir ); + CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qfalse, ENTITYNUM_WORLD ); + break; + + case EV_BULLET_HIT_FLESH: + DEBUGNAME("EV_BULLET_HIT_FLESH"); + CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qtrue, es->eventParm ); + break; + + case EV_SHOTGUN: + DEBUGNAME("EV_SHOTGUN"); + CG_ShotgunFire( es ); + break; + + case EV_GENERAL_SOUND: + DEBUGNAME("EV_GENERAL_SOUND"); + if ( cgs.gameSounds[ es->eventParm ] ) { + trap_S_StartSound (NULL, es->number, CHAN_VOICE, cgs.gameSounds[ es->eventParm ] ); + } else { + s = CG_ConfigString( CS_SOUNDS + es->eventParm ); + trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, s ) ); + } + break; + + case EV_GLOBAL_SOUND: // play from the player's head so it never diminishes + DEBUGNAME("EV_GLOBAL_SOUND"); + if ( cgs.gameSounds[ es->eventParm ] ) { + trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.gameSounds[ es->eventParm ] ); + } else { + s = CG_ConfigString( CS_SOUNDS + es->eventParm ); + trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, CG_CustomSound( es->number, s ) ); + } + break; + + case EV_GLOBAL_TEAM_SOUND: // play from the player's head so it never diminishes + { + DEBUGNAME("EV_GLOBAL_TEAM_SOUND"); + switch( es->eventParm ) { + case GTS_RED_CAPTURE: // CTF: red team captured the blue flag, 1FCTF: red team captured the neutral flag + if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED ) + CG_AddBufferedSound( cgs.media.captureYourTeamSound ); + else + CG_AddBufferedSound( cgs.media.captureOpponentSound ); + break; + case GTS_BLUE_CAPTURE: // CTF: blue team captured the red flag, 1FCTF: blue team captured the neutral flag + if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE ) + CG_AddBufferedSound( cgs.media.captureYourTeamSound ); + else + CG_AddBufferedSound( cgs.media.captureOpponentSound ); + break; + case GTS_RED_RETURN: // CTF: blue flag returned, 1FCTF: never used + if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED ) + CG_AddBufferedSound( cgs.media.returnYourTeamSound ); + else + CG_AddBufferedSound( cgs.media.returnOpponentSound ); + // + CG_AddBufferedSound( cgs.media.blueFlagReturnedSound ); + break; + case GTS_BLUE_RETURN: // CTF red flag returned, 1FCTF: neutral flag returned + if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE ) + CG_AddBufferedSound( cgs.media.returnYourTeamSound ); + else + CG_AddBufferedSound( cgs.media.returnOpponentSound ); + // + CG_AddBufferedSound( cgs.media.redFlagReturnedSound ); + break; + + case GTS_RED_TAKEN: // CTF: red team took blue flag, 1FCTF: blue team took the neutral flag + // if this player picked up the flag then a sound is played in CG_CheckLocalSounds + if (cg.snap->ps.powerups[PW_BLUEFLAG] || cg.snap->ps.powerups[PW_NEUTRALFLAG]) { + } + else { + if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) { +#ifdef MISSIONPACK + if (cgs.gametype == GT_1FCTF) + CG_AddBufferedSound( cgs.media.yourTeamTookTheFlagSound ); + else +#endif + CG_AddBufferedSound( cgs.media.enemyTookYourFlagSound ); + } + else if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) { +#ifdef MISSIONPACK + if (cgs.gametype == GT_1FCTF) + CG_AddBufferedSound( cgs.media.enemyTookTheFlagSound ); + else +#endif + CG_AddBufferedSound( cgs.media.yourTeamTookEnemyFlagSound ); + } + } + break; + case GTS_BLUE_TAKEN: // CTF: blue team took the red flag, 1FCTF red team took the neutral flag + // if this player picked up the flag then a sound is played in CG_CheckLocalSounds + if (cg.snap->ps.powerups[PW_REDFLAG] || cg.snap->ps.powerups[PW_NEUTRALFLAG]) { + } + else { + if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) { +#ifdef MISSIONPACK + if (cgs.gametype == GT_1FCTF) + CG_AddBufferedSound( cgs.media.yourTeamTookTheFlagSound ); + else +#endif + CG_AddBufferedSound( cgs.media.enemyTookYourFlagSound ); + } + else if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) { +#ifdef MISSIONPACK + if (cgs.gametype == GT_1FCTF) + CG_AddBufferedSound( cgs.media.enemyTookTheFlagSound ); + else +#endif + CG_AddBufferedSound( cgs.media.yourTeamTookEnemyFlagSound ); + } + } + break; + case GTS_REDOBELISK_ATTACKED: // Overload: red obelisk is being attacked + if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) { + CG_AddBufferedSound( cgs.media.yourBaseIsUnderAttackSound ); + } + break; + case GTS_BLUEOBELISK_ATTACKED: // Overload: blue obelisk is being attacked + if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) { + CG_AddBufferedSound( cgs.media.yourBaseIsUnderAttackSound ); + } + break; + + case GTS_REDTEAM_SCORED: + CG_AddBufferedSound(cgs.media.redScoredSound); + break; + case GTS_BLUETEAM_SCORED: + CG_AddBufferedSound(cgs.media.blueScoredSound); + break; + case GTS_REDTEAM_TOOK_LEAD: + CG_AddBufferedSound(cgs.media.redLeadsSound); + break; + case GTS_BLUETEAM_TOOK_LEAD: + CG_AddBufferedSound(cgs.media.blueLeadsSound); + break; + case GTS_TEAMS_ARE_TIED: + CG_AddBufferedSound( cgs.media.teamsTiedSound ); + break; +#ifdef MISSIONPACK + case GTS_KAMIKAZE: + trap_S_StartLocalSound(cgs.media.kamikazeFarSound, CHAN_ANNOUNCER); + break; +#endif + default: + break; + } + break; + } + + case EV_PAIN: + // local player sounds are triggered in CG_CheckLocalSounds, + // so ignore events on the player + DEBUGNAME("EV_PAIN"); + if ( cent->currentState.number != cg.snap->ps.clientNum ) { + CG_PainEvent( cent, es->eventParm ); + } + break; + + case EV_DEATH1: + case EV_DEATH2: + case EV_DEATH3: + DEBUGNAME("EV_DEATHx"); + trap_S_StartSound( NULL, es->number, CHAN_VOICE, + CG_CustomSound( es->number, va("*death%i.wav", event - EV_DEATH1 + 1) ) ); + break; + + + case EV_OBITUARY: + DEBUGNAME("EV_OBITUARY"); + CG_Obituary( es ); + break; + + // + // powerup events + // + case EV_POWERUP_QUAD: + DEBUGNAME("EV_POWERUP_QUAD"); + if ( es->number == cg.snap->ps.clientNum ) { + cg.powerupActive = PW_QUAD; + cg.powerupTime = cg.time; + } + trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.quadSound ); + break; + case EV_POWERUP_BATTLESUIT: + DEBUGNAME("EV_POWERUP_BATTLESUIT"); + if ( es->number == cg.snap->ps.clientNum ) { + cg.powerupActive = PW_BATTLESUIT; + cg.powerupTime = cg.time; + } + trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.protectSound ); + break; + case EV_POWERUP_REGEN: + DEBUGNAME("EV_POWERUP_REGEN"); + if ( es->number == cg.snap->ps.clientNum ) { + cg.powerupActive = PW_REGEN; + cg.powerupTime = cg.time; + } + trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.regenSound ); + break; + + case EV_GIB_PLAYER: + DEBUGNAME("EV_GIB_PLAYER"); + // don't play gib sound when using the kamikaze because it interferes + // with the kamikaze sound, downside is that the gib sound will also + // not be played when someone is gibbed while just carrying the kamikaze + if ( !(es->eFlags & EF_KAMIKAZE) ) { + trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.gibSound ); + } + CG_GibPlayer( cent->lerpOrigin ); + break; + + case EV_STOPLOOPINGSOUND: + DEBUGNAME("EV_STOPLOOPINGSOUND"); + trap_S_StopLoopingSound( es->number ); + es->loopSound = 0; + break; + + case EV_DEBUG_LINE: + DEBUGNAME("EV_DEBUG_LINE"); + CG_Beam( cent ); + break; + + default: + DEBUGNAME("UNKNOWN"); + CG_Error( "Unknown event: %i", event ); + break; + } + +} + + +/* +============== +CG_CheckEvents + +============== +*/ +void CG_CheckEvents( centity_t *cent ) { + // check for event-only entities + if ( cent->currentState.eType > ET_EVENTS ) { + if ( cent->previousEvent ) { + return; // already fired + } + // if this is a player event set the entity number of the client entity number + if ( cent->currentState.eFlags & EF_PLAYER_EVENT ) { + cent->currentState.number = cent->currentState.otherEntityNum; + } + + cent->previousEvent = 1; + + cent->currentState.event = cent->currentState.eType - ET_EVENTS; + } else { + // check for events riding with another entity + if ( cent->currentState.event == cent->previousEvent ) { + return; + } + cent->previousEvent = cent->currentState.event; + if ( ( cent->currentState.event & ~EV_EVENT_BITS ) == 0 ) { + return; + } + } + + // calculate the position at exactly the frame time + BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, cent->lerpOrigin ); + CG_SetEntitySoundPosition( cent ); + + CG_EntityEvent( cent, cent->lerpOrigin ); +} + diff --git a/reaction/engine/code/cgame/cg_info.c b/reaction/engine/code/cgame/cg_info.c new file mode 100644 index 00000000..fba60fb2 --- /dev/null +++ b/reaction/engine/code/cgame/cg_info.c @@ -0,0 +1,297 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_info.c -- display information while data is being loading + +#include "cg_local.h" + +#define MAX_LOADING_PLAYER_ICONS 16 +#define MAX_LOADING_ITEM_ICONS 26 + +static int loadingPlayerIconCount; +static int loadingItemIconCount; +static qhandle_t loadingPlayerIcons[MAX_LOADING_PLAYER_ICONS]; +static qhandle_t loadingItemIcons[MAX_LOADING_ITEM_ICONS]; + + +/* +=================== +CG_DrawLoadingIcons +=================== +*/ +static void CG_DrawLoadingIcons( void ) { + int n; + int x, y; + + for( n = 0; n < loadingPlayerIconCount; n++ ) { + x = 16 + n * 78; + y = 324-40; + CG_DrawPic( x, y, 64, 64, loadingPlayerIcons[n] ); + } + + for( n = 0; n < loadingItemIconCount; n++ ) { + y = 400-40; + if( n >= 13 ) { + y += 40; + } + x = 16 + n % 13 * 48; + CG_DrawPic( x, y, 32, 32, loadingItemIcons[n] ); + } +} + + +/* +====================== +CG_LoadingString + +====================== +*/ +void CG_LoadingString( const char *s ) { + Q_strncpyz( cg.infoScreenText, s, sizeof( cg.infoScreenText ) ); + + trap_UpdateScreen(); +} + +/* +=================== +CG_LoadingItem +=================== +*/ +void CG_LoadingItem( int itemNum ) { + gitem_t *item; + + item = &bg_itemlist[itemNum]; + + if ( item->icon && loadingItemIconCount < MAX_LOADING_ITEM_ICONS ) { + loadingItemIcons[loadingItemIconCount++] = trap_R_RegisterShaderNoMip( item->icon ); + } + + CG_LoadingString( item->pickup_name ); +} + +/* +=================== +CG_LoadingClient +=================== +*/ +void CG_LoadingClient( int clientNum ) { + const char *info; + char *skin; + char personality[MAX_QPATH]; + char model[MAX_QPATH]; + char iconName[MAX_QPATH]; + + info = CG_ConfigString( CS_PLAYERS + clientNum ); + + if ( loadingPlayerIconCount < MAX_LOADING_PLAYER_ICONS ) { + Q_strncpyz( model, Info_ValueForKey( info, "model" ), sizeof( model ) ); + skin = Q_strrchr( model, '/' ); + if ( skin ) { + *skin++ = '\0'; + } else { + skin = "default"; + } + + Com_sprintf( iconName, MAX_QPATH, "models/players/%s/icon_%s.tga", model, skin ); + + loadingPlayerIcons[loadingPlayerIconCount] = trap_R_RegisterShaderNoMip( iconName ); + if ( !loadingPlayerIcons[loadingPlayerIconCount] ) { + Com_sprintf( iconName, MAX_QPATH, "models/players/characters/%s/icon_%s.tga", model, skin ); + loadingPlayerIcons[loadingPlayerIconCount] = trap_R_RegisterShaderNoMip( iconName ); + } + if ( !loadingPlayerIcons[loadingPlayerIconCount] ) { + Com_sprintf( iconName, MAX_QPATH, "models/players/%s/icon_%s.tga", DEFAULT_MODEL, "default" ); + loadingPlayerIcons[loadingPlayerIconCount] = trap_R_RegisterShaderNoMip( iconName ); + } + if ( loadingPlayerIcons[loadingPlayerIconCount] ) { + loadingPlayerIconCount++; + } + } + + Q_strncpyz( personality, Info_ValueForKey( info, "n" ), sizeof(personality) ); + Q_CleanStr( personality ); + + if( cgs.gametype == GT_SINGLE_PLAYER ) { + trap_S_RegisterSound( va( "sound/player/announce/%s.wav", personality ), qtrue ); + } + + CG_LoadingString( personality ); +} + + +/* +==================== +CG_DrawInformation + +Draw all the status / pacifier stuff during level loading +==================== +*/ +void CG_DrawInformation( void ) { + const char *s; + const char *info; + const char *sysInfo; + int y; + int value; + qhandle_t levelshot; + qhandle_t detail; + char buf[1024]; + + info = CG_ConfigString( CS_SERVERINFO ); + sysInfo = CG_ConfigString( CS_SYSTEMINFO ); + + s = Info_ValueForKey( info, "mapname" ); + levelshot = trap_R_RegisterShaderNoMip( va( "levelshots/%s.tga", s ) ); + if ( !levelshot ) { + levelshot = trap_R_RegisterShaderNoMip( "menu/art/unknownmap" ); + } + trap_R_SetColor( NULL ); + CG_DrawPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, levelshot ); + + // blend a detail texture over it + detail = trap_R_RegisterShader( "levelShotDetail" ); + trap_R_DrawStretchPic( 0, 0, cgs.glconfig.vidWidth, cgs.glconfig.vidHeight, 0, 0, 2.5, 2, detail ); + + // draw the icons of things as they are loaded + CG_DrawLoadingIcons(); + + // the first 150 rows are reserved for the client connection + // screen to write into + if ( cg.infoScreenText[0] ) { + UI_DrawProportionalString( 320, 128-32, va("Loading... %s", cg.infoScreenText), + UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); + } else { + UI_DrawProportionalString( 320, 128-32, "Awaiting snapshot...", + UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); + } + + // draw info string information + + y = 180-32; + + // don't print server lines if playing a local game + trap_Cvar_VariableStringBuffer( "sv_running", buf, sizeof( buf ) ); + if ( !atoi( buf ) ) { + // server hostname + Q_strncpyz(buf, Info_ValueForKey( info, "sv_hostname" ), 1024); + Q_CleanStr(buf); + UI_DrawProportionalString( 320, y, buf, + UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); + y += PROP_HEIGHT; + + // pure server + s = Info_ValueForKey( sysInfo, "sv_pure" ); + if ( s[0] == '1' ) { + UI_DrawProportionalString( 320, y, "Pure Server", + UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); + y += PROP_HEIGHT; + } + + // server-specific message of the day + s = CG_ConfigString( CS_MOTD ); + if ( s[0] ) { + UI_DrawProportionalString( 320, y, s, + UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); + y += PROP_HEIGHT; + } + + // some extra space after hostname and motd + y += 10; + } + + // map-specific message (long map name) + s = CG_ConfigString( CS_MESSAGE ); + if ( s[0] ) { + UI_DrawProportionalString( 320, y, s, + UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); + y += PROP_HEIGHT; + } + + // cheats warning + s = Info_ValueForKey( sysInfo, "sv_cheats" ); + if ( s[0] == '1' ) { + UI_DrawProportionalString( 320, y, "CHEATS ARE ENABLED", + UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); + y += PROP_HEIGHT; + } + + // game type + switch ( cgs.gametype ) { + case GT_FFA: + s = "Free For All"; + break; + case GT_SINGLE_PLAYER: + s = "Single Player"; + break; + case GT_TOURNAMENT: + s = "Tournament"; + break; + case GT_TEAM: + s = "Team Deathmatch"; + break; + case GT_CTF: + s = "Capture The Flag"; + break; +#ifdef MISSIONPACK + case GT_1FCTF: + s = "One Flag CTF"; + break; + case GT_OBELISK: + s = "Overload"; + break; + case GT_HARVESTER: + s = "Harvester"; + break; +#endif + default: + s = "Unknown Gametype"; + break; + } + UI_DrawProportionalString( 320, y, s, + UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); + y += PROP_HEIGHT; + + value = atoi( Info_ValueForKey( info, "timelimit" ) ); + if ( value ) { + UI_DrawProportionalString( 320, y, va( "timelimit %i", value ), + UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); + y += PROP_HEIGHT; + } + + if (cgs.gametype < GT_CTF ) { + value = atoi( Info_ValueForKey( info, "fraglimit" ) ); + if ( value ) { + UI_DrawProportionalString( 320, y, va( "fraglimit %i", value ), + UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); + y += PROP_HEIGHT; + } + } + + if (cgs.gametype >= GT_CTF) { + value = atoi( Info_ValueForKey( info, "capturelimit" ) ); + if ( value ) { + UI_DrawProportionalString( 320, y, va( "capturelimit %i", value ), + UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); + y += PROP_HEIGHT; + } + } +} + diff --git a/reaction/engine/code/cgame/cg_local.h b/reaction/engine/code/cgame/cg_local.h new file mode 100644 index 00000000..d000916d --- /dev/null +++ b/reaction/engine/code/cgame/cg_local.h @@ -0,0 +1,1668 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +#include "../qcommon/q_shared.h" +#include "../renderer/tr_types.h" +#include "../game/bg_public.h" +#include "cg_public.h" + + +// The entire cgame module is unloaded and reloaded on each level change, +// so there is NO persistant data between levels on the client side. +// If you absolutely need something stored, it can either be kept +// by the server in the server stored userinfos, or stashed in a cvar. + +#ifdef MISSIONPACK +#define CG_FONT_THRESHOLD 0.1 +#endif + +#define POWERUP_BLINKS 5 + +#define POWERUP_BLINK_TIME 1000 +#define FADE_TIME 200 +#define PULSE_TIME 200 +#define DAMAGE_DEFLECT_TIME 100 +#define DAMAGE_RETURN_TIME 400 +#define DAMAGE_TIME 500 +#define LAND_DEFLECT_TIME 150 +#define LAND_RETURN_TIME 300 +#define STEP_TIME 200 +#define DUCK_TIME 100 +#define PAIN_TWITCH_TIME 200 +#define WEAPON_SELECT_TIME 1400 +#define ITEM_SCALEUP_TIME 1000 +#define ZOOM_TIME 150 +#define ITEM_BLOB_TIME 200 +#define MUZZLE_FLASH_TIME 20 +#define SINK_TIME 1000 // time for fragments to sink into ground before going away +#define ATTACKER_HEAD_TIME 10000 +#define REWARD_TIME 3000 + +#define PULSE_SCALE 1.5 // amount to scale up the icons when activating + +#define MAX_STEP_CHANGE 32 + +#define MAX_VERTS_ON_POLY 10 +#define MAX_MARK_POLYS 256 + +#define STAT_MINUS 10 // num frame for '-' stats digit + +#define ICON_SIZE 48 +#define CHAR_WIDTH 32 +#define CHAR_HEIGHT 48 +#define TEXT_ICON_SPACE 4 + +#define TEAMCHAT_WIDTH 80 +#define TEAMCHAT_HEIGHT 8 + +// very large characters +#define GIANT_WIDTH 32 +#define GIANT_HEIGHT 48 + +#define NUM_CROSSHAIRS 10 + +#define TEAM_OVERLAY_MAXNAME_WIDTH 12 +#define TEAM_OVERLAY_MAXLOCATION_WIDTH 16 + +#define DEFAULT_MODEL "sarge" +#ifdef MISSIONPACK +#define DEFAULT_TEAM_MODEL "james" +#define DEFAULT_TEAM_HEAD "*james" +#else +#define DEFAULT_TEAM_MODEL "sarge" +#define DEFAULT_TEAM_HEAD "sarge" +#endif + +#define DEFAULT_REDTEAM_NAME "Stroggs" +#define DEFAULT_BLUETEAM_NAME "Pagans" + +typedef enum { + FOOTSTEP_NORMAL, + FOOTSTEP_BOOT, + FOOTSTEP_FLESH, + FOOTSTEP_MECH, + FOOTSTEP_ENERGY, + FOOTSTEP_METAL, + FOOTSTEP_SPLASH, + + FOOTSTEP_TOTAL +} footstep_t; + +typedef enum { + IMPACTSOUND_DEFAULT, + IMPACTSOUND_METAL, + IMPACTSOUND_FLESH +} impactSound_t; + +//================================================= + +// player entities need to track more information +// than any other type of entity. + +// note that not every player entity is a client entity, +// because corpses after respawn are outside the normal +// client numbering range + +// when changing animation, set animationTime to frameTime + lerping time +// The current lerp will finish out, then it will lerp to the new animation +typedef struct { + int oldFrame; + int oldFrameTime; // time when ->oldFrame was exactly on + + int frame; + int frameTime; // time when ->frame will be exactly on + + float backlerp; + + float yawAngle; + qboolean yawing; + float pitchAngle; + qboolean pitching; + + int animationNumber; // may include ANIM_TOGGLEBIT + animation_t *animation; + int animationTime; // time when the first frame of the animation will be exact +} lerpFrame_t; + + +typedef struct { + lerpFrame_t legs, torso, flag; + int painTime; + int painDirection; // flip from 0 to 1 + int lightningFiring; + + // railgun trail spawning + vec3_t railgunImpact; + qboolean railgunFlash; + + // machinegun spinning + float barrelAngle; + int barrelTime; + qboolean barrelSpinning; +} playerEntity_t; + +//================================================= + + + +// centity_t have a direct corespondence with gentity_t in the game, but +// only the entityState_t is directly communicated to the cgame +typedef struct centity_s { + entityState_t currentState; // from cg.frame + entityState_t nextState; // from cg.nextFrame, if available + qboolean interpolate; // true if next is valid to interpolate to + qboolean currentValid; // true if cg.frame holds this entity + + int muzzleFlashTime; // move to playerEntity? + int previousEvent; + int teleportFlag; + + int trailTime; // so missile trails can handle dropped initial packets + int dustTrailTime; + int miscTime; + + int snapShotTime; // last time this entity was found in a snapshot + + playerEntity_t pe; + + int errorTime; // decay the error from this time + vec3_t errorOrigin; + vec3_t errorAngles; + + qboolean extrapolated; // false if origin / angles is an interpolation + vec3_t rawOrigin; + vec3_t rawAngles; + + vec3_t beamEnd; + + // exact interpolated position of entity on this frame + vec3_t lerpOrigin; + vec3_t lerpAngles; +} centity_t; + + +//====================================================================== + +// local entities are created as a result of events or predicted actions, +// and live independantly from all server transmitted entities + +typedef struct markPoly_s { + struct markPoly_s *prevMark, *nextMark; + int time; + qhandle_t markShader; + qboolean alphaFade; // fade alpha instead of rgb + float color[4]; + poly_t poly; + polyVert_t verts[MAX_VERTS_ON_POLY]; +} markPoly_t; + + +typedef enum { + LE_MARK, + LE_EXPLOSION, + LE_SPRITE_EXPLOSION, + LE_FRAGMENT, + LE_MOVE_SCALE_FADE, + LE_FALL_SCALE_FADE, + LE_FADE_RGB, + LE_SCALE_FADE, + LE_SCOREPLUM, +#ifdef MISSIONPACK + LE_KAMIKAZE, + LE_INVULIMPACT, + LE_INVULJUICED, + LE_SHOWREFENTITY +#endif +} leType_t; + +typedef enum { + LEF_PUFF_DONT_SCALE = 0x0001, // do not scale size over time + LEF_TUMBLE = 0x0002, // tumble over time, used for ejecting shells + LEF_SOUND1 = 0x0004, // sound 1 for kamikaze + LEF_SOUND2 = 0x0008 // sound 2 for kamikaze +} leFlag_t; + +typedef enum { + LEMT_NONE, + LEMT_BURN, + LEMT_BLOOD +} leMarkType_t; // fragment local entities can leave marks on walls + +typedef enum { + LEBS_NONE, + LEBS_BLOOD, + LEBS_BRASS +} leBounceSoundType_t; // fragment local entities can make sounds on impacts + +typedef struct localEntity_s { + struct localEntity_s *prev, *next; + leType_t leType; + int leFlags; + + int startTime; + int endTime; + int fadeInTime; + + float lifeRate; // 1.0 / (endTime - startTime) + + trajectory_t pos; + trajectory_t angles; + + float bounceFactor; // 0.0 = no bounce, 1.0 = perfect + + float color[4]; + + float radius; + + float light; + vec3_t lightColor; + + leMarkType_t leMarkType; // mark to leave on fragment impact + leBounceSoundType_t leBounceSoundType; + + refEntity_t refEntity; +} localEntity_t; + +//====================================================================== + + +typedef struct { + int client; + int score; + int ping; + int time; + int scoreFlags; + int powerUps; + int accuracy; + int impressiveCount; + int excellentCount; + int guantletCount; + int defendCount; + int assistCount; + int captures; + qboolean perfect; + int team; +} score_t; + +// each client has an associated clientInfo_t +// that contains media references necessary to present the +// client model and other color coded effects +// this is regenerated each time a client's configstring changes, +// usually as a result of a userinfo (name, model, etc) change +#define MAX_CUSTOM_SOUNDS 32 + +typedef struct { + qboolean infoValid; + + char name[MAX_QPATH]; + team_t team; + + int botSkill; // 0 = not bot, 1-5 = bot + + vec3_t color1; + vec3_t color2; + + int score; // updated by score servercmds + int location; // location index for team mode + int health; // you only get this info about your teammates + int armor; + int curWeapon; + + int handicap; + int wins, losses; // in tourney mode + + int teamTask; // task in teamplay (offence/defence) + qboolean teamLeader; // true when this is a team leader + + int powerups; // so can display quad/flag status + + int medkitUsageTime; + int invulnerabilityStartTime; + int invulnerabilityStopTime; + + int breathPuffTime; + + // when clientinfo is changed, the loading of models/skins/sounds + // can be deferred until you are dead, to prevent hitches in + // gameplay + char modelName[MAX_QPATH]; + char skinName[MAX_QPATH]; + char headModelName[MAX_QPATH]; + char headSkinName[MAX_QPATH]; + char redTeam[MAX_TEAMNAME]; + char blueTeam[MAX_TEAMNAME]; + qboolean deferred; + + qboolean newAnims; // true if using the new mission pack animations + qboolean fixedlegs; // true if legs yaw is always the same as torso yaw + qboolean fixedtorso; // true if torso never changes yaw + + vec3_t headOffset; // move head in icon views + footstep_t footsteps; + gender_t gender; // from model + + qhandle_t legsModel; + qhandle_t legsSkin; + + qhandle_t torsoModel; + qhandle_t torsoSkin; + + qhandle_t headModel; + qhandle_t headSkin; + + qhandle_t modelIcon; + + animation_t animations[MAX_TOTALANIMATIONS]; + + sfxHandle_t sounds[MAX_CUSTOM_SOUNDS]; +} clientInfo_t; + + +// each WP_* weapon enum has an associated weaponInfo_t +// that contains media references necessary to present the +// weapon and its effects +typedef struct weaponInfo_s { + qboolean registered; + gitem_t *item; + + qhandle_t handsModel; // the hands don't actually draw, they just position the weapon + qhandle_t weaponModel; + qhandle_t barrelModel; + qhandle_t flashModel; + + vec3_t weaponMidpoint; // so it will rotate centered instead of by tag + + float flashDlight; + vec3_t flashDlightColor; + sfxHandle_t flashSound[4]; // fast firing weapons randomly choose + + qhandle_t weaponIcon; + qhandle_t ammoIcon; + + qhandle_t ammoModel; + + qhandle_t missileModel; + sfxHandle_t missileSound; + void (*missileTrailFunc)( centity_t *, const struct weaponInfo_s *wi ); + float missileDlight; + vec3_t missileDlightColor; + int missileRenderfx; + + void (*ejectBrassFunc)( centity_t * ); + + float trailRadius; + float wiTrailTime; + + sfxHandle_t readySound; + sfxHandle_t firingSound; + qboolean loopFireSound; +} weaponInfo_t; + + +// each IT_* item has an associated itemInfo_t +// that constains media references necessary to present the +// item and its effects +typedef struct { + qboolean registered; + qhandle_t models[MAX_ITEM_MODELS]; + qhandle_t icon; +} itemInfo_t; + + +typedef struct { + int itemNum; +} powerupInfo_t; + + +#define MAX_SKULLTRAIL 10 + +typedef struct { + vec3_t positions[MAX_SKULLTRAIL]; + int numpositions; +} skulltrail_t; + + +#define MAX_REWARDSTACK 10 +#define MAX_SOUNDBUFFER 20 + +//====================================================================== + +// all cg.stepTime, cg.duckTime, cg.landTime, etc are set to cg.time when the action +// occurs, and they will have visible effects for #define STEP_TIME or whatever msec after + +#define MAX_PREDICTED_EVENTS 16 + +typedef struct { + int clientFrame; // incremented each frame + + int clientNum; + + qboolean demoPlayback; + qboolean levelShot; // taking a level menu screenshot + int deferredPlayerLoading; + qboolean loading; // don't defer players at initial startup + qboolean intermissionStarted; // don't play voice rewards, because game will end shortly + + // there are only one or two snapshot_t that are relevent at a time + int latestSnapshotNum; // the number of snapshots the client system has received + int latestSnapshotTime; // the time from latestSnapshotNum, so we don't need to read the snapshot yet + + snapshot_t *snap; // cg.snap->serverTime <= cg.time + snapshot_t *nextSnap; // cg.nextSnap->serverTime > cg.time, or NULL + snapshot_t activeSnapshots[2]; + + float frameInterpolation; // (float)( cg.time - cg.frame->serverTime ) / (cg.nextFrame->serverTime - cg.frame->serverTime) + + qboolean thisFrameTeleport; + qboolean nextFrameTeleport; + + int frametime; // cg.time - cg.oldTime + + int time; // this is the time value that the client + // is rendering at. + int oldTime; // time at last frame, used for missile trails and prediction checking + + int physicsTime; // either cg.snap->time or cg.nextSnap->time + + int timelimitWarnings; // 5 min, 1 min, overtime + int fraglimitWarnings; + + qboolean mapRestart; // set on a map restart to set back the weapon + + qboolean renderingThirdPerson; // during deaths, chasecams, etc + + // prediction state + qboolean hyperspace; // true if prediction has hit a trigger_teleport + playerState_t predictedPlayerState; + centity_t predictedPlayerEntity; + qboolean validPPS; // clear until the first call to CG_PredictPlayerState + int predictedErrorTime; + vec3_t predictedError; + + int eventSequence; + int predictableEvents[MAX_PREDICTED_EVENTS]; + + float stepChange; // for stair up smoothing + int stepTime; + + float duckChange; // for duck viewheight smoothing + int duckTime; + + float landChange; // for landing hard + int landTime; + + // input state sent to server + int weaponSelect; + + // auto rotating items + vec3_t autoAngles; + vec3_t autoAxis[3]; + vec3_t autoAnglesFast; + vec3_t autoAxisFast[3]; + + // view rendering + refdef_t refdef; + vec3_t refdefViewAngles; // will be converted to refdef.viewaxis + + // zoom key + qboolean zoomed; + int zoomTime; + float zoomSensitivity; + + // information screen text during loading + char infoScreenText[MAX_STRING_CHARS]; + + // scoreboard + int scoresRequestTime; + int numScores; + int selectedScore; + int teamScores[2]; + score_t scores[MAX_CLIENTS]; + qboolean showScores; + qboolean scoreBoardShowing; + int scoreFadeTime; + char killerName[MAX_NAME_LENGTH]; + char spectatorList[MAX_STRING_CHARS]; // list of names + int spectatorLen; // length of list + float spectatorWidth; // width in device units + int spectatorTime; // next time to offset + int spectatorPaintX; // current paint x + int spectatorPaintX2; // current paint x + int spectatorOffset; // current offset from start + int spectatorPaintLen; // current offset from start + + // skull trails + skulltrail_t skulltrails[MAX_CLIENTS]; + + // centerprinting + int centerPrintTime; + int centerPrintCharWidth; + int centerPrintY; + char centerPrint[1024]; + int centerPrintLines; + + // low ammo warning state + int lowAmmoWarning; // 1 = low, 2 = empty + + // kill timers for carnage reward + int lastKillTime; + + // crosshair client ID + int crosshairClientNum; + int crosshairClientTime; + + // powerup active flashing + int powerupActive; + int powerupTime; + + // attacking player + int attackerTime; + int voiceTime; + + // reward medals + int rewardStack; + int rewardTime; + int rewardCount[MAX_REWARDSTACK]; + qhandle_t rewardShader[MAX_REWARDSTACK]; + qhandle_t rewardSound[MAX_REWARDSTACK]; + + // sound buffer mainly for announcer sounds + int soundBufferIn; + int soundBufferOut; + int soundTime; + qhandle_t soundBuffer[MAX_SOUNDBUFFER]; + + // for voice chat buffer + int voiceChatTime; + int voiceChatBufferIn; + int voiceChatBufferOut; + + // warmup countdown + int warmup; + int warmupCount; + + //========================== + + int itemPickup; + int itemPickupTime; + int itemPickupBlendTime; // the pulse around the crosshair is timed seperately + + int weaponSelectTime; + int weaponAnimation; + int weaponAnimationTime; + + // blend blobs + float damageTime; + float damageX, damageY, damageValue; + + // status bar head + float headYaw; + float headEndPitch; + float headEndYaw; + int headEndTime; + float headStartPitch; + float headStartYaw; + int headStartTime; + + // view movement + float v_dmg_time; + float v_dmg_pitch; + float v_dmg_roll; + + vec3_t kick_angles; // weapon kicks + vec3_t kick_origin; + + // temp working variables for player view + float bobfracsin; + int bobcycle; + float xyspeed; + int nextOrbitTime; + + //qboolean cameraMode; // if rendering from a loaded camera + + + // development tool + refEntity_t testModelEntity; + char testModelName[MAX_QPATH]; + qboolean testGun; + +} cg_t; + + +// all of the model, shader, and sound references that are +// loaded at gamestate time are stored in cgMedia_t +// Other media that can be tied to clients, weapons, or items are +// stored in the clientInfo_t, itemInfo_t, weaponInfo_t, and powerupInfo_t +typedef struct { + qhandle_t charsetShader; + qhandle_t charsetProp; + qhandle_t charsetPropGlow; + qhandle_t charsetPropB; + qhandle_t whiteShader; + + qhandle_t redCubeModel; + qhandle_t blueCubeModel; + qhandle_t redCubeIcon; + qhandle_t blueCubeIcon; + qhandle_t redFlagModel; + qhandle_t blueFlagModel; + qhandle_t neutralFlagModel; + qhandle_t redFlagShader[3]; + qhandle_t blueFlagShader[3]; + qhandle_t flagShader[4]; + + qhandle_t flagPoleModel; + qhandle_t flagFlapModel; + + qhandle_t redFlagFlapSkin; + qhandle_t blueFlagFlapSkin; + qhandle_t neutralFlagFlapSkin; + + qhandle_t redFlagBaseModel; + qhandle_t blueFlagBaseModel; + qhandle_t neutralFlagBaseModel; + +#ifdef MISSIONPACK + qhandle_t overloadBaseModel; + qhandle_t overloadTargetModel; + qhandle_t overloadLightsModel; + qhandle_t overloadEnergyModel; + + qhandle_t harvesterModel; + qhandle_t harvesterRedSkin; + qhandle_t harvesterBlueSkin; + qhandle_t harvesterNeutralModel; +#endif + + qhandle_t armorModel; + qhandle_t armorIcon; + + qhandle_t teamStatusBar; + + qhandle_t deferShader; + + // gib explosions + qhandle_t gibAbdomen; + qhandle_t gibArm; + qhandle_t gibChest; + qhandle_t gibFist; + qhandle_t gibFoot; + qhandle_t gibForearm; + qhandle_t gibIntestine; + qhandle_t gibLeg; + qhandle_t gibSkull; + qhandle_t gibBrain; + + qhandle_t smoke2; + + qhandle_t machinegunBrassModel; + qhandle_t shotgunBrassModel; + + qhandle_t railRingsShader; + qhandle_t railCoreShader; + + qhandle_t lightningShader; + + qhandle_t friendShader; + + qhandle_t balloonShader; + qhandle_t connectionShader; + + qhandle_t selectShader; + qhandle_t viewBloodShader; + qhandle_t tracerShader; + qhandle_t crosshairShader[NUM_CROSSHAIRS]; + qhandle_t lagometerShader; + qhandle_t backTileShader; + qhandle_t noammoShader; + + qhandle_t smokePuffShader; + qhandle_t smokePuffRageProShader; + qhandle_t shotgunSmokePuffShader; + qhandle_t plasmaBallShader; + qhandle_t waterBubbleShader; + qhandle_t bloodTrailShader; +#ifdef MISSIONPACK + qhandle_t nailPuffShader; + qhandle_t blueProxMine; +#endif + + qhandle_t numberShaders[11]; + + qhandle_t shadowMarkShader; + + qhandle_t botSkillShaders[5]; + + // wall mark shaders + qhandle_t wakeMarkShader; + qhandle_t bloodMarkShader; + qhandle_t bulletMarkShader; + qhandle_t burnMarkShader; + qhandle_t holeMarkShader; + qhandle_t energyMarkShader; + + // powerup shaders + qhandle_t quadShader; + qhandle_t redQuadShader; + qhandle_t quadWeaponShader; + qhandle_t invisShader; + qhandle_t regenShader; + qhandle_t battleSuitShader; + qhandle_t battleWeaponShader; + qhandle_t hastePuffShader; + qhandle_t redKamikazeShader; + qhandle_t blueKamikazeShader; + + // weapon effect models + qhandle_t bulletFlashModel; + qhandle_t ringFlashModel; + qhandle_t dishFlashModel; + qhandle_t lightningExplosionModel; + + // weapon effect shaders + qhandle_t railExplosionShader; + qhandle_t plasmaExplosionShader; + qhandle_t bulletExplosionShader; + qhandle_t rocketExplosionShader; + qhandle_t grenadeExplosionShader; + qhandle_t bfgExplosionShader; + qhandle_t bloodExplosionShader; + + // special effects models + qhandle_t teleportEffectModel; + qhandle_t teleportEffectShader; +#ifdef MISSIONPACK + qhandle_t kamikazeEffectModel; + qhandle_t kamikazeShockWave; + qhandle_t kamikazeHeadModel; + qhandle_t kamikazeHeadTrail; + qhandle_t guardPowerupModel; + qhandle_t scoutPowerupModel; + qhandle_t doublerPowerupModel; + qhandle_t ammoRegenPowerupModel; + qhandle_t invulnerabilityImpactModel; + qhandle_t invulnerabilityJuicedModel; + qhandle_t medkitUsageModel; + qhandle_t dustPuffShader; + qhandle_t heartShader; +#endif + qhandle_t invulnerabilityPowerupModel; + + // scoreboard headers + qhandle_t scoreboardName; + qhandle_t scoreboardPing; + qhandle_t scoreboardScore; + qhandle_t scoreboardTime; + + // medals shown during gameplay + qhandle_t medalImpressive; + qhandle_t medalExcellent; + qhandle_t medalGauntlet; + qhandle_t medalDefend; + qhandle_t medalAssist; + qhandle_t medalCapture; + + // sounds + sfxHandle_t quadSound; + sfxHandle_t tracerSound; + sfxHandle_t selectSound; + sfxHandle_t useNothingSound; + sfxHandle_t wearOffSound; + sfxHandle_t footsteps[FOOTSTEP_TOTAL][4]; + sfxHandle_t sfx_lghit1; + sfxHandle_t sfx_lghit2; + sfxHandle_t sfx_lghit3; + sfxHandle_t sfx_ric1; + sfxHandle_t sfx_ric2; + sfxHandle_t sfx_ric3; + sfxHandle_t sfx_railg; + sfxHandle_t sfx_rockexp; + sfxHandle_t sfx_plasmaexp; +#ifdef MISSIONPACK + sfxHandle_t sfx_proxexp; + sfxHandle_t sfx_nghit; + sfxHandle_t sfx_nghitflesh; + sfxHandle_t sfx_nghitmetal; + sfxHandle_t sfx_chghit; + sfxHandle_t sfx_chghitflesh; + sfxHandle_t sfx_chghitmetal; + sfxHandle_t kamikazeExplodeSound; + sfxHandle_t kamikazeImplodeSound; + sfxHandle_t kamikazeFarSound; + sfxHandle_t useInvulnerabilitySound; + sfxHandle_t invulnerabilityImpactSound1; + sfxHandle_t invulnerabilityImpactSound2; + sfxHandle_t invulnerabilityImpactSound3; + sfxHandle_t invulnerabilityJuicedSound; + sfxHandle_t obeliskHitSound1; + sfxHandle_t obeliskHitSound2; + sfxHandle_t obeliskHitSound3; + sfxHandle_t obeliskRespawnSound; + sfxHandle_t winnerSound; + sfxHandle_t loserSound; + sfxHandle_t youSuckSound; +#endif + sfxHandle_t gibSound; + sfxHandle_t gibBounce1Sound; + sfxHandle_t gibBounce2Sound; + sfxHandle_t gibBounce3Sound; + sfxHandle_t teleInSound; + sfxHandle_t teleOutSound; + sfxHandle_t noAmmoSound; + sfxHandle_t respawnSound; + sfxHandle_t talkSound; + sfxHandle_t landSound; + sfxHandle_t fallSound; + sfxHandle_t jumpPadSound; + + sfxHandle_t oneMinuteSound; + sfxHandle_t fiveMinuteSound; + sfxHandle_t suddenDeathSound; + + sfxHandle_t threeFragSound; + sfxHandle_t twoFragSound; + sfxHandle_t oneFragSound; + + sfxHandle_t hitSound; + sfxHandle_t hitSoundHighArmor; + sfxHandle_t hitSoundLowArmor; + sfxHandle_t hitTeamSound; + sfxHandle_t impressiveSound; + sfxHandle_t excellentSound; + sfxHandle_t deniedSound; + sfxHandle_t humiliationSound; + sfxHandle_t assistSound; + sfxHandle_t defendSound; + sfxHandle_t firstImpressiveSound; + sfxHandle_t firstExcellentSound; + sfxHandle_t firstHumiliationSound; + + sfxHandle_t takenLeadSound; + sfxHandle_t tiedLeadSound; + sfxHandle_t lostLeadSound; + + sfxHandle_t voteNow; + sfxHandle_t votePassed; + sfxHandle_t voteFailed; + + sfxHandle_t watrInSound; + sfxHandle_t watrOutSound; + sfxHandle_t watrUnSound; + + sfxHandle_t flightSound; + sfxHandle_t medkitSound; + + sfxHandle_t weaponHoverSound; + + // teamplay sounds + sfxHandle_t captureAwardSound; + sfxHandle_t redScoredSound; + sfxHandle_t blueScoredSound; + sfxHandle_t redLeadsSound; + sfxHandle_t blueLeadsSound; + sfxHandle_t teamsTiedSound; + + sfxHandle_t captureYourTeamSound; + sfxHandle_t captureOpponentSound; + sfxHandle_t returnYourTeamSound; + sfxHandle_t returnOpponentSound; + sfxHandle_t takenYourTeamSound; + sfxHandle_t takenOpponentSound; + + sfxHandle_t redFlagReturnedSound; + sfxHandle_t blueFlagReturnedSound; + sfxHandle_t neutralFlagReturnedSound; + sfxHandle_t enemyTookYourFlagSound; + sfxHandle_t enemyTookTheFlagSound; + sfxHandle_t yourTeamTookEnemyFlagSound; + sfxHandle_t yourTeamTookTheFlagSound; + sfxHandle_t youHaveFlagSound; + sfxHandle_t yourBaseIsUnderAttackSound; + sfxHandle_t holyShitSound; + + // tournament sounds + sfxHandle_t count3Sound; + sfxHandle_t count2Sound; + sfxHandle_t count1Sound; + sfxHandle_t countFightSound; + sfxHandle_t countPrepareSound; + +#ifdef MISSIONPACK + // new stuff + qhandle_t patrolShader; + qhandle_t assaultShader; + qhandle_t campShader; + qhandle_t followShader; + qhandle_t defendShader; + qhandle_t teamLeaderShader; + qhandle_t retrieveShader; + qhandle_t escortShader; + qhandle_t flagShaders[3]; + sfxHandle_t countPrepareTeamSound; + + sfxHandle_t ammoregenSound; + sfxHandle_t doublerSound; + sfxHandle_t guardSound; + sfxHandle_t scoutSound; +#endif + qhandle_t cursor; + qhandle_t selectCursor; + qhandle_t sizeCursor; + + sfxHandle_t regenSound; + sfxHandle_t protectSound; + sfxHandle_t n_healthSound; + sfxHandle_t hgrenb1aSound; + sfxHandle_t hgrenb2aSound; + sfxHandle_t wstbimplSound; + sfxHandle_t wstbimpmSound; + sfxHandle_t wstbimpdSound; + sfxHandle_t wstbactvSound; + +} cgMedia_t; + + +// The client game static (cgs) structure hold everything +// loaded or calculated from the gamestate. It will NOT +// be cleared when a tournement restart is done, allowing +// all clients to begin playing instantly +typedef struct { + gameState_t gameState; // gamestate from server + glconfig_t glconfig; // rendering configuration + float screenXScale; // derived from glconfig + float screenYScale; + float screenXBias; + + int serverCommandSequence; // reliable command stream counter + int processedSnapshotNum;// the number of snapshots cgame has requested + + qboolean localServer; // detected on startup by checking sv_running + + // parsed from serverinfo + gametype_t gametype; + int dmflags; + int teamflags; + int fraglimit; + int capturelimit; + int timelimit; + int maxclients; + char mapname[MAX_QPATH]; + char redTeam[MAX_QPATH]; + char blueTeam[MAX_QPATH]; + + int voteTime; + int voteYes; + int voteNo; + qboolean voteModified; // beep whenever changed + char voteString[MAX_STRING_TOKENS]; + + int teamVoteTime[2]; + int teamVoteYes[2]; + int teamVoteNo[2]; + qboolean teamVoteModified[2]; // beep whenever changed + char teamVoteString[2][MAX_STRING_TOKENS]; + + int levelStartTime; + + int scores1, scores2; // from configstrings + int redflag, blueflag; // flag status from configstrings + int flagStatus; + + qboolean newHud; + + // + // locally derived information from gamestate + // + qhandle_t gameModels[MAX_MODELS]; + sfxHandle_t gameSounds[MAX_SOUNDS]; + + int numInlineModels; + qhandle_t inlineDrawModel[MAX_MODELS]; + vec3_t inlineModelMidpoints[MAX_MODELS]; + + clientInfo_t clientinfo[MAX_CLIENTS]; + + // teamchat width is *3 because of embedded color codes + char teamChatMsgs[TEAMCHAT_HEIGHT][TEAMCHAT_WIDTH*3+1]; + int teamChatMsgTimes[TEAMCHAT_HEIGHT]; + int teamChatPos; + int teamLastChatPos; + + int cursorX; + int cursorY; + qboolean eventHandling; + qboolean mouseCaptured; + qboolean sizingHud; + void *capturedItem; + qhandle_t activeCursor; + + // orders + int currentOrder; + qboolean orderPending; + int orderTime; + int currentVoiceClient; + int acceptOrderTime; + int acceptTask; + int acceptLeader; + char acceptVoice[MAX_NAME_LENGTH]; + + // media + cgMedia_t media; + +} cgs_t; + +//============================================================================== + +extern cgs_t cgs; +extern cg_t cg; +extern centity_t cg_entities[MAX_GENTITIES]; +extern weaponInfo_t cg_weapons[MAX_WEAPONS]; +extern itemInfo_t cg_items[MAX_ITEMS]; +extern markPoly_t cg_markPolys[MAX_MARK_POLYS]; + +extern vmCvar_t cg_centertime; +extern vmCvar_t cg_runpitch; +extern vmCvar_t cg_runroll; +extern vmCvar_t cg_bobup; +extern vmCvar_t cg_bobpitch; +extern vmCvar_t cg_bobroll; +extern vmCvar_t cg_swingSpeed; +extern vmCvar_t cg_shadows; +extern vmCvar_t cg_gibs; +extern vmCvar_t cg_drawTimer; +extern vmCvar_t cg_drawFPS; +extern vmCvar_t cg_drawSnapshot; +extern vmCvar_t cg_draw3dIcons; +extern vmCvar_t cg_drawIcons; +extern vmCvar_t cg_drawAmmoWarning; +extern vmCvar_t cg_drawCrosshair; +extern vmCvar_t cg_drawCrosshairNames; +extern vmCvar_t cg_drawRewards; +extern vmCvar_t cg_drawTeamOverlay; +extern vmCvar_t cg_teamOverlayUserinfo; +extern vmCvar_t cg_crosshairX; +extern vmCvar_t cg_crosshairY; +extern vmCvar_t cg_crosshairSize; +extern vmCvar_t cg_crosshairHealth; +extern vmCvar_t cg_drawStatus; +extern vmCvar_t cg_draw2D; +extern vmCvar_t cg_animSpeed; +extern vmCvar_t cg_debugAnim; +extern vmCvar_t cg_debugPosition; +extern vmCvar_t cg_debugEvents; +extern vmCvar_t cg_railTrailTime; +extern vmCvar_t cg_errorDecay; +extern vmCvar_t cg_nopredict; +extern vmCvar_t cg_noPlayerAnims; +extern vmCvar_t cg_showmiss; +extern vmCvar_t cg_footsteps; +extern vmCvar_t cg_addMarks; +extern vmCvar_t cg_brassTime; +extern vmCvar_t cg_gun_frame; +extern vmCvar_t cg_gun_x; +extern vmCvar_t cg_gun_y; +extern vmCvar_t cg_gun_z; +extern vmCvar_t cg_drawGun; +extern vmCvar_t cg_viewsize; +extern vmCvar_t cg_tracerChance; +extern vmCvar_t cg_tracerWidth; +extern vmCvar_t cg_tracerLength; +extern vmCvar_t cg_autoswitch; +extern vmCvar_t cg_ignore; +extern vmCvar_t cg_simpleItems; +extern vmCvar_t cg_fov; +extern vmCvar_t cg_zoomFov; +extern vmCvar_t cg_thirdPersonRange; +extern vmCvar_t cg_thirdPersonAngle; +extern vmCvar_t cg_thirdPerson; +extern vmCvar_t cg_lagometer; +extern vmCvar_t cg_drawAttacker; +extern vmCvar_t cg_synchronousClients; +extern vmCvar_t cg_teamChatTime; +extern vmCvar_t cg_teamChatHeight; +extern vmCvar_t cg_stats; +extern vmCvar_t cg_forceModel; +extern vmCvar_t cg_buildScript; +extern vmCvar_t cg_paused; +extern vmCvar_t cg_blood; +extern vmCvar_t cg_predictItems; +extern vmCvar_t cg_deferPlayers; +extern vmCvar_t cg_drawFriend; +extern vmCvar_t cg_teamChatsOnly; +extern vmCvar_t cg_noVoiceChats; +extern vmCvar_t cg_noVoiceText; +extern vmCvar_t cg_scorePlum; +extern vmCvar_t cg_smoothClients; +extern vmCvar_t pmove_fixed; +extern vmCvar_t pmove_msec; +//extern vmCvar_t cg_pmove_fixed; +extern vmCvar_t cg_cameraOrbit; +extern vmCvar_t cg_cameraOrbitDelay; +extern vmCvar_t cg_timescaleFadeEnd; +extern vmCvar_t cg_timescaleFadeSpeed; +extern vmCvar_t cg_timescale; +extern vmCvar_t cg_cameraMode; +extern vmCvar_t cg_smallFont; +extern vmCvar_t cg_bigFont; +extern vmCvar_t cg_noTaunt; +extern vmCvar_t cg_noProjectileTrail; +extern vmCvar_t cg_oldRail; +extern vmCvar_t cg_oldRocket; +extern vmCvar_t cg_oldPlasma; +extern vmCvar_t cg_trueLightning; +#ifdef MISSIONPACK +extern vmCvar_t cg_redTeamName; +extern vmCvar_t cg_blueTeamName; +extern vmCvar_t cg_currentSelectedPlayer; +extern vmCvar_t cg_currentSelectedPlayerName; +extern vmCvar_t cg_singlePlayer; +extern vmCvar_t cg_enableDust; +extern vmCvar_t cg_enableBreath; +extern vmCvar_t cg_singlePlayerActive; +extern vmCvar_t cg_recordSPDemo; +extern vmCvar_t cg_recordSPDemoName; +extern vmCvar_t cg_obeliskRespawnDelay; +#endif + +// +// cg_main.c +// +const char *CG_ConfigString( int index ); +const char *CG_Argv( int arg ); + +void QDECL CG_Printf( const char *msg, ... ); +void QDECL CG_Error( const char *msg, ... ); + +void CG_StartMusic( void ); + +void CG_UpdateCvars( void ); + +int CG_CrosshairPlayer( void ); +int CG_LastAttacker( void ); +void CG_LoadMenus(const char *menuFile); +void CG_KeyEvent(int key, qboolean down); +void CG_MouseEvent(int x, int y); +void CG_EventHandling(int type); +void CG_RankRunFrame( void ); +void CG_SetScoreSelection(void *menu); +score_t *CG_GetSelectedScore( void ); +void CG_BuildSpectatorString( void ); + + +// +// cg_view.c +// +void CG_TestModel_f (void); +void CG_TestGun_f (void); +void CG_TestModelNextFrame_f (void); +void CG_TestModelPrevFrame_f (void); +void CG_TestModelNextSkin_f (void); +void CG_TestModelPrevSkin_f (void); +void CG_ZoomDown_f( void ); +void CG_ZoomUp_f( void ); +void CG_AddBufferedSound( sfxHandle_t sfx); + +void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ); + + +// +// cg_drawtools.c +// +void CG_AdjustFrom640( float *x, float *y, float *w, float *h ); +void CG_FillRect( float x, float y, float width, float height, const float *color ); +void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void CG_DrawString( float x, float y, const char *string, + float charWidth, float charHeight, const float *modulate ); + + +void CG_DrawStringExt( int x, int y, const char *string, const float *setColor, + qboolean forceColor, qboolean shadow, int charWidth, int charHeight, int maxChars ); +void CG_DrawBigString( int x, int y, const char *s, float alpha ); +void CG_DrawBigStringColor( int x, int y, const char *s, vec4_t color ); +void CG_DrawSmallString( int x, int y, const char *s, float alpha ); +void CG_DrawSmallStringColor( int x, int y, const char *s, vec4_t color ); + +int CG_DrawStrlen( const char *str ); + +float *CG_FadeColor( int startMsec, int totalMsec ); +float *CG_TeamColor( int team ); +void CG_TileClear( void ); +void CG_ColorForHealth( vec4_t hcolor ); +void CG_GetColorForHealth( int health, int armor, vec4_t hcolor ); + +void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color ); +void CG_DrawRect( float x, float y, float width, float height, float size, const float *color ); +void CG_DrawSides(float x, float y, float w, float h, float size); +void CG_DrawTopBottom(float x, float y, float w, float h, float size); + + +// +// cg_draw.c, cg_newDraw.c +// +extern int sortedTeamPlayers[TEAM_MAXOVERLAY]; +extern int numSortedTeamPlayers; +extern int drawTeamOverlayModificationCount; +extern char systemChat[256]; +extern char teamChat1[256]; +extern char teamChat2[256]; + +void CG_AddLagometerFrameInfo( void ); +void CG_AddLagometerSnapshotInfo( snapshot_t *snap ); +void CG_CenterPrint( const char *str, int y, int charWidth ); +void CG_DrawHead( float x, float y, float w, float h, int clientNum, vec3_t headAngles ); +void CG_DrawActive( stereoFrame_t stereoView ); +void CG_DrawFlagModel( float x, float y, float w, float h, int team, qboolean force2D ); +void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team ); +void CG_OwnerDraw(float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader, int textStyle); +void CG_Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style); +int CG_Text_Width(const char *text, float scale, int limit); +int CG_Text_Height(const char *text, float scale, int limit); +void CG_SelectPrevPlayer( void ); +void CG_SelectNextPlayer( void ); +float CG_GetValue(int ownerDraw); +qboolean CG_OwnerDrawVisible(int flags); +void CG_RunMenuScript(char **args); +void CG_ShowResponseHead( void ); +void CG_SetPrintString(int type, const char *p); +void CG_InitTeamChat( void ); +void CG_GetTeamColor(vec4_t *color); +const char *CG_GetGameStatusText( void ); +const char *CG_GetKillerText( void ); +void CG_Draw3DModel(float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles); +void CG_Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader); +void CG_CheckOrderPending( void ); +const char *CG_GameTypeString( void ); +qboolean CG_YourTeamHasFlag( void ); +qboolean CG_OtherTeamHasFlag( void ); +qhandle_t CG_StatusHandle(int task); + + + +// +// cg_player.c +// +void CG_Player( centity_t *cent ); +void CG_ResetPlayerEntity( centity_t *cent ); +void CG_AddRefEntityWithPowerups( refEntity_t *ent, entityState_t *state, int team ); +void CG_NewClientInfo( int clientNum ); +sfxHandle_t CG_CustomSound( int clientNum, const char *soundName ); + +// +// cg_predict.c +// +void CG_BuildSolidList( void ); +int CG_PointContents( const vec3_t point, int passEntityNum ); +void CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, + int skipNumber, int mask ); +void CG_PredictPlayerState( void ); +void CG_LoadDeferredPlayers( void ); + + +// +// cg_events.c +// +void CG_CheckEvents( centity_t *cent ); +const char *CG_PlaceString( int rank ); +void CG_EntityEvent( centity_t *cent, vec3_t position ); +void CG_PainEvent( centity_t *cent, int health ); + + +// +// cg_ents.c +// +void CG_SetEntitySoundPosition( centity_t *cent ); +void CG_AddPacketEntities( void ); +void CG_Beam( centity_t *cent ); +void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ); + +void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, + qhandle_t parentModel, char *tagName ); +void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, + qhandle_t parentModel, char *tagName ); + + + +// +// cg_weapons.c +// +void CG_NextWeapon_f( void ); +void CG_PrevWeapon_f( void ); +void CG_Weapon_f( void ); + +void CG_RegisterWeapon( int weaponNum ); +void CG_RegisterItemVisuals( int itemNum ); + +void CG_FireWeapon( centity_t *cent ); +void CG_MissileHitWall( int weapon, int clientNum, vec3_t origin, vec3_t dir, impactSound_t soundType ); +void CG_MissileHitPlayer( int weapon, vec3_t origin, vec3_t dir, int entityNum ); +void CG_ShotgunFire( entityState_t *es ); +void CG_Bullet( vec3_t origin, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum ); + +void CG_RailTrail( clientInfo_t *ci, vec3_t start, vec3_t end ); +void CG_GrappleTrail( centity_t *ent, const weaponInfo_t *wi ); +void CG_AddViewWeapon (playerState_t *ps); +void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent, int team ); +void CG_DrawWeaponSelect( void ); + +void CG_OutOfAmmoChange( void ); // should this be in pmove? + +// +// cg_marks.c +// +void CG_InitMarkPolys( void ); +void CG_AddMarks( void ); +void CG_ImpactMark( qhandle_t markShader, + const vec3_t origin, const vec3_t dir, + float orientation, + float r, float g, float b, float a, + qboolean alphaFade, + float radius, qboolean temporary ); + +// +// cg_localents.c +// +void CG_InitLocalEntities( void ); +localEntity_t *CG_AllocLocalEntity( void ); +void CG_AddLocalEntities( void ); + +// +// cg_effects.c +// +localEntity_t *CG_SmokePuff( const vec3_t p, + const vec3_t vel, + float radius, + float r, float g, float b, float a, + float duration, + int startTime, + int fadeInTime, + int leFlags, + qhandle_t hShader ); +void CG_BubbleTrail( vec3_t start, vec3_t end, float spacing ); +void CG_SpawnEffect( vec3_t org ); +#ifdef MISSIONPACK +void CG_KamikazeEffect( vec3_t org ); +void CG_ObeliskExplode( vec3_t org, int entityNum ); +void CG_ObeliskPain( vec3_t org ); +void CG_InvulnerabilityImpact( vec3_t org, vec3_t angles ); +void CG_InvulnerabilityJuiced( vec3_t org ); +void CG_LightningBoltBeam( vec3_t start, vec3_t end ); +#endif +void CG_ScorePlum( int client, vec3_t org, int score ); + +void CG_GibPlayer( vec3_t playerOrigin ); +void CG_BigExplode( vec3_t playerOrigin ); + +void CG_Bleed( vec3_t origin, int entityNum ); + +localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir, + qhandle_t hModel, qhandle_t shader, int msec, + qboolean isSprite ); + +// +// cg_snapshot.c +// +void CG_ProcessSnapshots( void ); + +// +// cg_info.c +// +void CG_LoadingString( const char *s ); +void CG_LoadingItem( int itemNum ); +void CG_LoadingClient( int clientNum ); +void CG_DrawInformation( void ); + +// +// cg_scoreboard.c +// +qboolean CG_DrawOldScoreboard( void ); +void CG_DrawOldTourneyScoreboard( void ); + +// +// cg_consolecmds.c +// +qboolean CG_ConsoleCommand( void ); +void CG_InitConsoleCommands( void ); + +// +// cg_servercmds.c +// +void CG_ExecuteNewServerCommands( int latestSequence ); +void CG_ParseServerinfo( void ); +void CG_SetConfigValues( void ); +void CG_LoadVoiceChats( void ); +void CG_ShaderStateChanged(void); +void CG_VoiceChatLocal( int mode, qboolean voiceOnly, int clientNum, int color, const char *cmd ); +void CG_PlayBufferedVoiceChats( void ); + +// +// cg_playerstate.c +// +void CG_Respawn( void ); +void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops ); +void CG_CheckChangedPredictableEvents( playerState_t *ps ); + + +//=============================================== + +// +// system traps +// These functions are how the cgame communicates with the main game system +// + +// print message on the local console +void trap_Print( const char *fmt ); + +// abort the game +void trap_Error( const char *fmt ); + +// milliseconds should only be used for performance tuning, never +// for anything game related. Get time from the CG_DrawActiveFrame parameter +int trap_Milliseconds( void ); + +// console variable interaction +void trap_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ); +void trap_Cvar_Update( vmCvar_t *vmCvar ); +void trap_Cvar_Set( const char *var_name, const char *value ); +void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); + +// ServerCommand and ConsoleCommand parameter access +int trap_Argc( void ); +void trap_Argv( int n, char *buffer, int bufferLength ); +void trap_Args( char *buffer, int bufferLength ); + +// filesystem access +// returns length of file +int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ); +void trap_FS_Read( void *buffer, int len, fileHandle_t f ); +void trap_FS_Write( const void *buffer, int len, fileHandle_t f ); +void trap_FS_FCloseFile( fileHandle_t f ); +int trap_FS_Seek( fileHandle_t f, long offset, int origin ); // fsOrigin_t + +// add commands to the local console as if they were typed in +// for map changing, etc. The command is not executed immediately, +// but will be executed in order the next time console commands +// are processed +void trap_SendConsoleCommand( const char *text ); + +// register a command name so the console can perform command completion. +// FIXME: replace this with a normal console command "defineCommand"? +void trap_AddCommand( const char *cmdName ); + +// send a string to the server over the network +void trap_SendClientCommand( const char *s ); + +// force a screen update, only used during gamestate load +void trap_UpdateScreen( void ); + +// model collision +void trap_CM_LoadMap( const char *mapname ); +int trap_CM_NumInlineModels( void ); +clipHandle_t trap_CM_InlineModel( int index ); // 0 = world, 1+ = bmodels +clipHandle_t trap_CM_TempBoxModel( const vec3_t mins, const vec3_t maxs ); +int trap_CM_PointContents( const vec3_t p, clipHandle_t model ); +int trap_CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles ); +void trap_CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end, + const vec3_t mins, const vec3_t maxs, + clipHandle_t model, int brushmask ); +void trap_CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end, + const vec3_t mins, const vec3_t maxs, + clipHandle_t model, int brushmask, + const vec3_t origin, const vec3_t angles ); + +// Returns the projection of a polygon onto the solid brushes in the world +int trap_CM_MarkFragments( int numPoints, const vec3_t *points, + const vec3_t projection, + int maxPoints, vec3_t pointBuffer, + int maxFragments, markFragment_t *fragmentBuffer ); + +// normal sounds will have their volume dynamically changed as their entity +// moves and the listener moves +void trap_S_StartSound( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx ); +void trap_S_StopLoopingSound(int entnum); + +// a local sound is always played full volume +void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum ); +void trap_S_ClearLoopingSounds( qboolean killall ); +void trap_S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); +void trap_S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); +void trap_S_UpdateEntityPosition( int entityNum, const vec3_t origin ); + +// respatialize recalculates the volumes of sound as they should be heard by the +// given entityNum and position +void trap_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ); +sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed ); // returns buzz if not found +void trap_S_StartBackgroundTrack( const char *intro, const char *loop ); // empty name stops music +void trap_S_StopBackgroundTrack( void ); + + +void trap_R_LoadWorldMap( const char *mapname ); + +// all media should be registered during level startup to prevent +// hitches during gameplay +qhandle_t trap_R_RegisterModel( const char *name ); // returns rgb axis if not found +qhandle_t trap_R_RegisterSkin( const char *name ); // returns all white if not found +qhandle_t trap_R_RegisterShader( const char *name ); // returns all white if not found +qhandle_t trap_R_RegisterShaderNoMip( const char *name ); // returns all white if not found + +// a scene is built up by calls to R_ClearScene and the various R_Add functions. +// Nothing is drawn until R_RenderScene is called. +void trap_R_ClearScene( void ); +void trap_R_AddRefEntityToScene( const refEntity_t *re ); + +// polys are intended for simple wall marks, not really for doing +// significant construction +void trap_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts ); +void trap_R_AddPolysToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int numPolys ); +void trap_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ); +int trap_R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ); +void trap_R_RenderScene( const refdef_t *fd ); +void trap_R_SetColor( const float *rgba ); // NULL = 1,1,1,1 +void trap_R_DrawStretchPic( float x, float y, float w, float h, + float s1, float t1, float s2, float t2, qhandle_t hShader ); +void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ); +int trap_R_LerpTag( orientation_t *tag, clipHandle_t mod, int startFrame, int endFrame, + float frac, const char *tagName ); +void trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ); + +// The glconfig_t will not change during the life of a cgame. +// If it needs to change, the entire cgame will be restarted, because +// all the qhandle_t are then invalid. +void trap_GetGlconfig( glconfig_t *glconfig ); + +// the gamestate should be grabbed at startup, and whenever a +// configstring changes +void trap_GetGameState( gameState_t *gamestate ); + +// cgame will poll each frame to see if a newer snapshot has arrived +// that it is interested in. The time is returned seperately so that +// snapshot latency can be calculated. +void trap_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ); + +// a snapshot get can fail if the snapshot (or the entties it holds) is so +// old that it has fallen out of the client system queue +qboolean trap_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ); + +// retrieve a text command from the server stream +// the current snapshot will hold the number of the most recent command +// qfalse can be returned if the client system handled the command +// argc() / argv() can be used to examine the parameters of the command +qboolean trap_GetServerCommand( int serverCommandNumber ); + +// returns the most recent command number that can be passed to GetUserCmd +// this will always be at least one higher than the number in the current +// snapshot, and it may be quite a few higher if it is a fast computer on +// a lagged connection +int trap_GetCurrentCmdNumber( void ); + +qboolean trap_GetUserCmd( int cmdNumber, usercmd_t *ucmd ); + +// used for the weapon select and zoom +void trap_SetUserCmdValue( int stateValue, float sensitivityScale ); + +// aids for VM testing +void testPrintInt( char *string, int i ); +void testPrintFloat( char *string, float f ); + +int trap_MemoryRemaining( void ); +void trap_R_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font); +qboolean trap_Key_IsDown( int keynum ); +int trap_Key_GetCatcher( void ); +void trap_Key_SetCatcher( int catcher ); +int trap_Key_GetKey( const char *binding ); + + +typedef enum { + SYSTEM_PRINT, + CHAT_PRINT, + TEAMCHAT_PRINT +} q3print_t; + + +int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits); +e_status trap_CIN_StopCinematic(int handle); +e_status trap_CIN_RunCinematic (int handle); +void trap_CIN_DrawCinematic (int handle); +void trap_CIN_SetExtents (int handle, int x, int y, int w, int h); + +void trap_SnapVector( float *v ); + +qboolean trap_loadCamera(const char *name); +void trap_startCamera(int time); +qboolean trap_getCameraInfo(int time, vec3_t *origin, vec3_t *angles); + +qboolean trap_GetEntityToken( char *buffer, int bufferSize ); + +void CG_ClearParticles (void); +void CG_AddParticles (void); +void CG_ParticleSnow (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum); +void CG_ParticleSmoke (qhandle_t pshader, centity_t *cent); +void CG_AddParticleShrapnel (localEntity_t *le); +void CG_ParticleSnowFlurry (qhandle_t pshader, centity_t *cent); +void CG_ParticleBulletDebris (vec3_t org, vec3_t vel, int duration); +void CG_ParticleSparks (vec3_t org, vec3_t vel, int duration, float x, float y, float speed); +void CG_ParticleDust (centity_t *cent, vec3_t origin, vec3_t dir); +void CG_ParticleMisc (qhandle_t pshader, vec3_t origin, int size, int duration, float alpha); +void CG_ParticleExplosion (char *animStr, vec3_t origin, vec3_t vel, int duration, int sizeStart, int sizeEnd); +extern qboolean initparticles; +int CG_NewParticleArea ( int num ); + + diff --git a/reaction/engine/code/cgame/cg_localents.c b/reaction/engine/code/cgame/cg_localents.c new file mode 100644 index 00000000..8077cbdd --- /dev/null +++ b/reaction/engine/code/cgame/cg_localents.c @@ -0,0 +1,886 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +// cg_localents.c -- every frame, generate renderer commands for locally +// processed entities, like smoke puffs, gibs, shells, etc. + +#include "cg_local.h" + +#define MAX_LOCAL_ENTITIES 512 +localEntity_t cg_localEntities[MAX_LOCAL_ENTITIES]; +localEntity_t cg_activeLocalEntities; // double linked list +localEntity_t *cg_freeLocalEntities; // single linked list + +/* +=================== +CG_InitLocalEntities + +This is called at startup and for tournement restarts +=================== +*/ +void CG_InitLocalEntities( void ) { + int i; + + memset( cg_localEntities, 0, sizeof( cg_localEntities ) ); + cg_activeLocalEntities.next = &cg_activeLocalEntities; + cg_activeLocalEntities.prev = &cg_activeLocalEntities; + cg_freeLocalEntities = cg_localEntities; + for ( i = 0 ; i < MAX_LOCAL_ENTITIES - 1 ; i++ ) { + cg_localEntities[i].next = &cg_localEntities[i+1]; + } +} + + +/* +================== +CG_FreeLocalEntity +================== +*/ +void CG_FreeLocalEntity( localEntity_t *le ) { + if ( !le->prev ) { + CG_Error( "CG_FreeLocalEntity: not active" ); + } + + // remove from the doubly linked active list + le->prev->next = le->next; + le->next->prev = le->prev; + + // the free list is only singly linked + le->next = cg_freeLocalEntities; + cg_freeLocalEntities = le; +} + +/* +=================== +CG_AllocLocalEntity + +Will allways succeed, even if it requires freeing an old active entity +=================== +*/ +localEntity_t *CG_AllocLocalEntity( void ) { + localEntity_t *le; + + if ( !cg_freeLocalEntities ) { + // no free entities, so free the one at the end of the chain + // remove the oldest active entity + CG_FreeLocalEntity( cg_activeLocalEntities.prev ); + } + + le = cg_freeLocalEntities; + cg_freeLocalEntities = cg_freeLocalEntities->next; + + memset( le, 0, sizeof( *le ) ); + + // link into the active list + le->next = cg_activeLocalEntities.next; + le->prev = &cg_activeLocalEntities; + cg_activeLocalEntities.next->prev = le; + cg_activeLocalEntities.next = le; + return le; +} + + +/* +==================================================================================== + +FRAGMENT PROCESSING + +A fragment localentity interacts with the environment in some way (hitting walls), +or generates more localentities along a trail. + +==================================================================================== +*/ + +/* +================ +CG_BloodTrail + +Leave expanding blood puffs behind gibs +================ +*/ +void CG_BloodTrail( localEntity_t *le ) { + int t; + int t2; + int step; + vec3_t newOrigin; + localEntity_t *blood; + + step = 150; + t = step * ( (cg.time - cg.frametime + step ) / step ); + t2 = step * ( cg.time / step ); + + for ( ; t <= t2; t += step ) { + BG_EvaluateTrajectory( &le->pos, t, newOrigin ); + + blood = CG_SmokePuff( newOrigin, vec3_origin, + 20, // radius + 1, 1, 1, 1, // color + 2000, // trailTime + t, // startTime + 0, // fadeInTime + 0, // flags + cgs.media.bloodTrailShader ); + // use the optimized version + blood->leType = LE_FALL_SCALE_FADE; + // drop a total of 40 units over its lifetime + blood->pos.trDelta[2] = 40; + } +} + + +/* +================ +CG_FragmentBounceMark +================ +*/ +void CG_FragmentBounceMark( localEntity_t *le, trace_t *trace ) { + int radius; + + if ( le->leMarkType == LEMT_BLOOD ) { + + radius = 16 + (rand()&31); + CG_ImpactMark( cgs.media.bloodMarkShader, trace->endpos, trace->plane.normal, random()*360, + 1,1,1,1, qtrue, radius, qfalse ); + } else if ( le->leMarkType == LEMT_BURN ) { + + radius = 8 + (rand()&15); + CG_ImpactMark( cgs.media.burnMarkShader, trace->endpos, trace->plane.normal, random()*360, + 1,1,1,1, qtrue, radius, qfalse ); + } + + + // don't allow a fragment to make multiple marks, or they + // pile up while settling + le->leMarkType = LEMT_NONE; +} + +/* +================ +CG_FragmentBounceSound +================ +*/ +void CG_FragmentBounceSound( localEntity_t *le, trace_t *trace ) { + if ( le->leBounceSoundType == LEBS_BLOOD ) { + // half the gibs will make splat sounds + if ( rand() & 1 ) { + int r = rand()&3; + sfxHandle_t s; + + if ( r == 0 ) { + s = cgs.media.gibBounce1Sound; + } else if ( r == 1 ) { + s = cgs.media.gibBounce2Sound; + } else { + s = cgs.media.gibBounce3Sound; + } + trap_S_StartSound( trace->endpos, ENTITYNUM_WORLD, CHAN_AUTO, s ); + } + } else if ( le->leBounceSoundType == LEBS_BRASS ) { + + } + + // don't allow a fragment to make multiple bounce sounds, + // or it gets too noisy as they settle + le->leBounceSoundType = LEBS_NONE; +} + + +/* +================ +CG_ReflectVelocity +================ +*/ +void CG_ReflectVelocity( localEntity_t *le, trace_t *trace ) { + vec3_t velocity; + float dot; + int hitTime; + + // reflect the velocity on the trace plane + hitTime = cg.time - cg.frametime + cg.frametime * trace->fraction; + BG_EvaluateTrajectoryDelta( &le->pos, hitTime, velocity ); + dot = DotProduct( velocity, trace->plane.normal ); + VectorMA( velocity, -2*dot, trace->plane.normal, le->pos.trDelta ); + + VectorScale( le->pos.trDelta, le->bounceFactor, le->pos.trDelta ); + + VectorCopy( trace->endpos, le->pos.trBase ); + le->pos.trTime = cg.time; + + + // check for stop, making sure that even on low FPS systems it doesn't bobble + if ( trace->allsolid || + ( trace->plane.normal[2] > 0 && + ( le->pos.trDelta[2] < 40 || le->pos.trDelta[2] < -cg.frametime * le->pos.trDelta[2] ) ) ) { + le->pos.trType = TR_STATIONARY; + } else { + + } +} + +/* +================ +CG_AddFragment +================ +*/ +void CG_AddFragment( localEntity_t *le ) { + vec3_t newOrigin; + trace_t trace; + + if ( le->pos.trType == TR_STATIONARY ) { + // sink into the ground if near the removal time + int t; + float oldZ; + + t = le->endTime - cg.time; + if ( t < SINK_TIME ) { + // we must use an explicit lighting origin, otherwise the + // lighting would be lost as soon as the origin went + // into the ground + VectorCopy( le->refEntity.origin, le->refEntity.lightingOrigin ); + le->refEntity.renderfx |= RF_LIGHTING_ORIGIN; + oldZ = le->refEntity.origin[2]; + le->refEntity.origin[2] -= 16 * ( 1.0 - (float)t / SINK_TIME ); + trap_R_AddRefEntityToScene( &le->refEntity ); + le->refEntity.origin[2] = oldZ; + } else { + trap_R_AddRefEntityToScene( &le->refEntity ); + } + + return; + } + + // calculate new position + BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin ); + + // trace a line from previous position to new position + CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID ); + if ( trace.fraction == 1.0 ) { + // still in free fall + VectorCopy( newOrigin, le->refEntity.origin ); + + if ( le->leFlags & LEF_TUMBLE ) { + vec3_t angles; + + BG_EvaluateTrajectory( &le->angles, cg.time, angles ); + AnglesToAxis( angles, le->refEntity.axis ); + } + + trap_R_AddRefEntityToScene( &le->refEntity ); + + // add a blood trail + if ( le->leBounceSoundType == LEBS_BLOOD ) { + CG_BloodTrail( le ); + } + + return; + } + + // if it is in a nodrop zone, remove it + // this keeps gibs from waiting at the bottom of pits of death + // and floating levels + if ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { + CG_FreeLocalEntity( le ); + return; + } + + // leave a mark + CG_FragmentBounceMark( le, &trace ); + + // do a bouncy sound + CG_FragmentBounceSound( le, &trace ); + + // reflect the velocity on the trace plane + CG_ReflectVelocity( le, &trace ); + + trap_R_AddRefEntityToScene( &le->refEntity ); +} + +/* +===================================================================== + +TRIVIAL LOCAL ENTITIES + +These only do simple scaling or modulation before passing to the renderer +===================================================================== +*/ + +/* +==================== +CG_AddFadeRGB +==================== +*/ +void CG_AddFadeRGB( localEntity_t *le ) { + refEntity_t *re; + float c; + + re = &le->refEntity; + + c = ( le->endTime - cg.time ) * le->lifeRate; + c *= 0xff; + + re->shaderRGBA[0] = le->color[0] * c; + re->shaderRGBA[1] = le->color[1] * c; + re->shaderRGBA[2] = le->color[2] * c; + re->shaderRGBA[3] = le->color[3] * c; + + trap_R_AddRefEntityToScene( re ); +} + +/* +================== +CG_AddMoveScaleFade +================== +*/ +static void CG_AddMoveScaleFade( localEntity_t *le ) { + refEntity_t *re; + float c; + vec3_t delta; + float len; + + re = &le->refEntity; + + if ( le->fadeInTime > le->startTime && cg.time < le->fadeInTime ) { + // fade / grow time + c = 1.0 - (float) ( le->fadeInTime - cg.time ) / ( le->fadeInTime - le->startTime ); + } + else { + // fade / grow time + c = ( le->endTime - cg.time ) * le->lifeRate; + } + + re->shaderRGBA[3] = 0xff * c * le->color[3]; + + if ( !( le->leFlags & LEF_PUFF_DONT_SCALE ) ) { + re->radius = le->radius * ( 1.0 - c ) + 8; + } + + BG_EvaluateTrajectory( &le->pos, cg.time, re->origin ); + + // if the view would be "inside" the sprite, kill the sprite + // so it doesn't add too much overdraw + VectorSubtract( re->origin, cg.refdef.vieworg, delta ); + len = VectorLength( delta ); + if ( len < le->radius ) { + CG_FreeLocalEntity( le ); + return; + } + + trap_R_AddRefEntityToScene( re ); +} + + +/* +=================== +CG_AddScaleFade + +For rocket smokes that hang in place, fade out, and are +removed if the view passes through them. +There are often many of these, so it needs to be simple. +=================== +*/ +static void CG_AddScaleFade( localEntity_t *le ) { + refEntity_t *re; + float c; + vec3_t delta; + float len; + + re = &le->refEntity; + + // fade / grow time + c = ( le->endTime - cg.time ) * le->lifeRate; + + re->shaderRGBA[3] = 0xff * c * le->color[3]; + re->radius = le->radius * ( 1.0 - c ) + 8; + + // if the view would be "inside" the sprite, kill the sprite + // so it doesn't add too much overdraw + VectorSubtract( re->origin, cg.refdef.vieworg, delta ); + len = VectorLength( delta ); + if ( len < le->radius ) { + CG_FreeLocalEntity( le ); + return; + } + + trap_R_AddRefEntityToScene( re ); +} + + +/* +================= +CG_AddFallScaleFade + +This is just an optimized CG_AddMoveScaleFade +For blood mists that drift down, fade out, and are +removed if the view passes through them. +There are often 100+ of these, so it needs to be simple. +================= +*/ +static void CG_AddFallScaleFade( localEntity_t *le ) { + refEntity_t *re; + float c; + vec3_t delta; + float len; + + re = &le->refEntity; + + // fade time + c = ( le->endTime - cg.time ) * le->lifeRate; + + re->shaderRGBA[3] = 0xff * c * le->color[3]; + + re->origin[2] = le->pos.trBase[2] - ( 1.0 - c ) * le->pos.trDelta[2]; + + re->radius = le->radius * ( 1.0 - c ) + 16; + + // if the view would be "inside" the sprite, kill the sprite + // so it doesn't add too much overdraw + VectorSubtract( re->origin, cg.refdef.vieworg, delta ); + len = VectorLength( delta ); + if ( len < le->radius ) { + CG_FreeLocalEntity( le ); + return; + } + + trap_R_AddRefEntityToScene( re ); +} + + + +/* +================ +CG_AddExplosion +================ +*/ +static void CG_AddExplosion( localEntity_t *ex ) { + refEntity_t *ent; + + ent = &ex->refEntity; + + // add the entity + trap_R_AddRefEntityToScene(ent); + + // add the dlight + if ( ex->light ) { + float light; + + light = (float)( cg.time - ex->startTime ) / ( ex->endTime - ex->startTime ); + if ( light < 0.5 ) { + light = 1.0; + } else { + light = 1.0 - ( light - 0.5 ) * 2; + } + light = ex->light * light; + trap_R_AddLightToScene(ent->origin, light, ex->lightColor[0], ex->lightColor[1], ex->lightColor[2] ); + } +} + +/* +================ +CG_AddSpriteExplosion +================ +*/ +static void CG_AddSpriteExplosion( localEntity_t *le ) { + refEntity_t re; + float c; + + re = le->refEntity; + + c = ( le->endTime - cg.time ) / ( float ) ( le->endTime - le->startTime ); + if ( c > 1 ) { + c = 1.0; // can happen during connection problems + } + + re.shaderRGBA[0] = 0xff; + re.shaderRGBA[1] = 0xff; + re.shaderRGBA[2] = 0xff; + re.shaderRGBA[3] = 0xff * c * 0.33; + + re.reType = RT_SPRITE; + re.radius = 42 * ( 1.0 - c ) + 30; + + trap_R_AddRefEntityToScene( &re ); + + // add the dlight + if ( le->light ) { + float light; + + light = (float)( cg.time - le->startTime ) / ( le->endTime - le->startTime ); + if ( light < 0.5 ) { + light = 1.0; + } else { + light = 1.0 - ( light - 0.5 ) * 2; + } + light = le->light * light; + trap_R_AddLightToScene(re.origin, light, le->lightColor[0], le->lightColor[1], le->lightColor[2] ); + } +} + + +#ifdef MISSIONPACK +/* +==================== +CG_AddKamikaze +==================== +*/ +void CG_AddKamikaze( localEntity_t *le ) { + refEntity_t *re; + refEntity_t shockwave; + float c; + vec3_t test, axis[3]; + int t; + + re = &le->refEntity; + + t = cg.time - le->startTime; + VectorClear( test ); + AnglesToAxis( test, axis ); + + if (t > KAMI_SHOCKWAVE_STARTTIME && t < KAMI_SHOCKWAVE_ENDTIME) { + + if (!(le->leFlags & LEF_SOUND1)) { +// trap_S_StartSound (re->origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.kamikazeExplodeSound ); + trap_S_StartLocalSound(cgs.media.kamikazeExplodeSound, CHAN_AUTO); + le->leFlags |= LEF_SOUND1; + } + // 1st kamikaze shockwave + memset(&shockwave, 0, sizeof(shockwave)); + shockwave.hModel = cgs.media.kamikazeShockWave; + shockwave.reType = RT_MODEL; + shockwave.shaderTime = re->shaderTime; + VectorCopy(re->origin, shockwave.origin); + + c = (float)(t - KAMI_SHOCKWAVE_STARTTIME) / (float)(KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVE_STARTTIME); + VectorScale( axis[0], c * KAMI_SHOCKWAVE_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[0] ); + VectorScale( axis[1], c * KAMI_SHOCKWAVE_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[1] ); + VectorScale( axis[2], c * KAMI_SHOCKWAVE_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[2] ); + shockwave.nonNormalizedAxes = qtrue; + + if (t > KAMI_SHOCKWAVEFADE_STARTTIME) { + c = (float)(t - KAMI_SHOCKWAVEFADE_STARTTIME) / (float)(KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVEFADE_STARTTIME); + } + else { + c = 0; + } + c *= 0xff; + shockwave.shaderRGBA[0] = 0xff - c; + shockwave.shaderRGBA[1] = 0xff - c; + shockwave.shaderRGBA[2] = 0xff - c; + shockwave.shaderRGBA[3] = 0xff - c; + + trap_R_AddRefEntityToScene( &shockwave ); + } + + if (t > KAMI_EXPLODE_STARTTIME && t < KAMI_IMPLODE_ENDTIME) { + // explosion and implosion + c = ( le->endTime - cg.time ) * le->lifeRate; + c *= 0xff; + re->shaderRGBA[0] = le->color[0] * c; + re->shaderRGBA[1] = le->color[1] * c; + re->shaderRGBA[2] = le->color[2] * c; + re->shaderRGBA[3] = le->color[3] * c; + + if( t < KAMI_IMPLODE_STARTTIME ) { + c = (float)(t - KAMI_EXPLODE_STARTTIME) / (float)(KAMI_IMPLODE_STARTTIME - KAMI_EXPLODE_STARTTIME); + } + else { + if (!(le->leFlags & LEF_SOUND2)) { +// trap_S_StartSound (re->origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.kamikazeImplodeSound ); + trap_S_StartLocalSound(cgs.media.kamikazeImplodeSound, CHAN_AUTO); + le->leFlags |= LEF_SOUND2; + } + c = (float)(KAMI_IMPLODE_ENDTIME - t) / (float) (KAMI_IMPLODE_ENDTIME - KAMI_IMPLODE_STARTTIME); + } + VectorScale( axis[0], c * KAMI_BOOMSPHERE_MAXRADIUS / KAMI_BOOMSPHEREMODEL_RADIUS, re->axis[0] ); + VectorScale( axis[1], c * KAMI_BOOMSPHERE_MAXRADIUS / KAMI_BOOMSPHEREMODEL_RADIUS, re->axis[1] ); + VectorScale( axis[2], c * KAMI_BOOMSPHERE_MAXRADIUS / KAMI_BOOMSPHEREMODEL_RADIUS, re->axis[2] ); + re->nonNormalizedAxes = qtrue; + + trap_R_AddRefEntityToScene( re ); + // add the dlight + trap_R_AddLightToScene( re->origin, c * 1000.0, 1.0, 1.0, c ); + } + + if (t > KAMI_SHOCKWAVE2_STARTTIME && t < KAMI_SHOCKWAVE2_ENDTIME) { + // 2nd kamikaze shockwave + if (le->angles.trBase[0] == 0 && + le->angles.trBase[1] == 0 && + le->angles.trBase[2] == 0) { + le->angles.trBase[0] = random() * 360; + le->angles.trBase[1] = random() * 360; + le->angles.trBase[2] = random() * 360; + } + else { + c = 0; + } + memset(&shockwave, 0, sizeof(shockwave)); + shockwave.hModel = cgs.media.kamikazeShockWave; + shockwave.reType = RT_MODEL; + shockwave.shaderTime = re->shaderTime; + VectorCopy(re->origin, shockwave.origin); + + test[0] = le->angles.trBase[0]; + test[1] = le->angles.trBase[1]; + test[2] = le->angles.trBase[2]; + AnglesToAxis( test, axis ); + + c = (float)(t - KAMI_SHOCKWAVE2_STARTTIME) / (float)(KAMI_SHOCKWAVE2_ENDTIME - KAMI_SHOCKWAVE2_STARTTIME); + VectorScale( axis[0], c * KAMI_SHOCKWAVE2_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[0] ); + VectorScale( axis[1], c * KAMI_SHOCKWAVE2_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[1] ); + VectorScale( axis[2], c * KAMI_SHOCKWAVE2_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[2] ); + shockwave.nonNormalizedAxes = qtrue; + + if (t > KAMI_SHOCKWAVE2FADE_STARTTIME) { + c = (float)(t - KAMI_SHOCKWAVE2FADE_STARTTIME) / (float)(KAMI_SHOCKWAVE2_ENDTIME - KAMI_SHOCKWAVE2FADE_STARTTIME); + } + else { + c = 0; + } + c *= 0xff; + shockwave.shaderRGBA[0] = 0xff - c; + shockwave.shaderRGBA[1] = 0xff - c; + shockwave.shaderRGBA[2] = 0xff - c; + shockwave.shaderRGBA[3] = 0xff - c; + + trap_R_AddRefEntityToScene( &shockwave ); + } +} + +/* +=================== +CG_AddInvulnerabilityImpact +=================== +*/ +void CG_AddInvulnerabilityImpact( localEntity_t *le ) { + trap_R_AddRefEntityToScene( &le->refEntity ); +} + +/* +=================== +CG_AddInvulnerabilityJuiced +=================== +*/ +void CG_AddInvulnerabilityJuiced( localEntity_t *le ) { + int t; + + t = cg.time - le->startTime; + if ( t > 3000 ) { + le->refEntity.axis[0][0] = (float) 1.0 + 0.3 * (t - 3000) / 2000; + le->refEntity.axis[1][1] = (float) 1.0 + 0.3 * (t - 3000) / 2000; + le->refEntity.axis[2][2] = (float) 0.7 + 0.3 * (2000 - (t - 3000)) / 2000; + } + if ( t > 5000 ) { + le->endTime = 0; + CG_GibPlayer( le->refEntity.origin ); + } + else { + trap_R_AddRefEntityToScene( &le->refEntity ); + } +} + +/* +=================== +CG_AddRefEntity +=================== +*/ +void CG_AddRefEntity( localEntity_t *le ) { + if (le->endTime < cg.time) { + CG_FreeLocalEntity( le ); + return; + } + trap_R_AddRefEntityToScene( &le->refEntity ); +} + +#endif +/* +=================== +CG_AddScorePlum +=================== +*/ +#define NUMBER_SIZE 8 + +void CG_AddScorePlum( localEntity_t *le ) { + refEntity_t *re; + vec3_t origin, delta, dir, vec, up = {0, 0, 1}; + float c, len; + int i, score, digits[10], numdigits, negative; + + re = &le->refEntity; + + c = ( le->endTime - cg.time ) * le->lifeRate; + + score = le->radius; + if (score < 0) { + re->shaderRGBA[0] = 0xff; + re->shaderRGBA[1] = 0x11; + re->shaderRGBA[2] = 0x11; + } + else { + re->shaderRGBA[0] = 0xff; + re->shaderRGBA[1] = 0xff; + re->shaderRGBA[2] = 0xff; + if (score >= 50) { + re->shaderRGBA[1] = 0; + } else if (score >= 20) { + re->shaderRGBA[0] = re->shaderRGBA[1] = 0; + } else if (score >= 10) { + re->shaderRGBA[2] = 0; + } else if (score >= 2) { + re->shaderRGBA[0] = re->shaderRGBA[2] = 0; + } + + } + if (c < 0.25) + re->shaderRGBA[3] = 0xff * 4 * c; + else + re->shaderRGBA[3] = 0xff; + + re->radius = NUMBER_SIZE / 2; + + VectorCopy(le->pos.trBase, origin); + origin[2] += 110 - c * 100; + + VectorSubtract(cg.refdef.vieworg, origin, dir); + CrossProduct(dir, up, vec); + VectorNormalize(vec); + + VectorMA(origin, -10 + 20 * sin(c * 2 * M_PI), vec, origin); + + // if the view would be "inside" the sprite, kill the sprite + // so it doesn't add too much overdraw + VectorSubtract( origin, cg.refdef.vieworg, delta ); + len = VectorLength( delta ); + if ( len < 20 ) { + CG_FreeLocalEntity( le ); + return; + } + + negative = qfalse; + if (score < 0) { + negative = qtrue; + score = -score; + } + + for (numdigits = 0; !(numdigits && !score); numdigits++) { + digits[numdigits] = score % 10; + score = score / 10; + } + + if (negative) { + digits[numdigits] = 10; + numdigits++; + } + + for (i = 0; i < numdigits; i++) { + VectorMA(origin, (float) (((float) numdigits / 2) - i) * NUMBER_SIZE, vec, re->origin); + re->customShader = cgs.media.numberShaders[digits[numdigits-1-i]]; + trap_R_AddRefEntityToScene( re ); + } +} + + + + +//============================================================================== + +/* +=================== +CG_AddLocalEntities + +=================== +*/ +void CG_AddLocalEntities( void ) { + localEntity_t *le, *next; + + // walk the list backwards, so any new local entities generated + // (trails, marks, etc) will be present this frame + le = cg_activeLocalEntities.prev; + for ( ; le != &cg_activeLocalEntities ; le = next ) { + // grab next now, so if the local entity is freed we + // still have it + next = le->prev; + + if ( cg.time >= le->endTime ) { + CG_FreeLocalEntity( le ); + continue; + } + switch ( le->leType ) { + default: + CG_Error( "Bad leType: %i", le->leType ); + break; + + case LE_MARK: + break; + + case LE_SPRITE_EXPLOSION: + CG_AddSpriteExplosion( le ); + break; + + case LE_EXPLOSION: + CG_AddExplosion( le ); + break; + + case LE_FRAGMENT: // gibs and brass + CG_AddFragment( le ); + break; + + case LE_MOVE_SCALE_FADE: // water bubbles + CG_AddMoveScaleFade( le ); + break; + + case LE_FADE_RGB: // teleporters, railtrails + CG_AddFadeRGB( le ); + break; + + case LE_FALL_SCALE_FADE: // gib blood trails + CG_AddFallScaleFade( le ); + break; + + case LE_SCALE_FADE: // rocket trails + CG_AddScaleFade( le ); + break; + + case LE_SCOREPLUM: + CG_AddScorePlum( le ); + break; + +#ifdef MISSIONPACK + case LE_KAMIKAZE: + CG_AddKamikaze( le ); + break; + case LE_INVULIMPACT: + CG_AddInvulnerabilityImpact( le ); + break; + case LE_INVULJUICED: + CG_AddInvulnerabilityJuiced( le ); + break; + case LE_SHOWREFENTITY: + CG_AddRefEntity( le ); + break; +#endif + } + } +} + + + + diff --git a/reaction/engine/code/cgame/cg_main.c b/reaction/engine/code/cgame/cg_main.c new file mode 100644 index 00000000..15a5539b --- /dev/null +++ b/reaction/engine/code/cgame/cg_main.c @@ -0,0 +1,1988 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_main.c -- initialization and primary entry point for cgame +#include "cg_local.h" + +#ifdef MISSIONPACK +#include "../ui/ui_shared.h" +// display context for new ui stuff +displayContextDef_t cgDC; +#endif + +int forceModelModificationCount = -1; + +void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ); +void CG_Shutdown( void ); + + +/* +================ +vmMain + +This is the only way control passes into the module. +This must be the very first function compiled into the .q3vm file +================ +*/ +intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) { + + switch ( command ) { + case CG_INIT: + CG_Init( arg0, arg1, arg2 ); + return 0; + case CG_SHUTDOWN: + CG_Shutdown(); + return 0; + case CG_CONSOLE_COMMAND: + return CG_ConsoleCommand(); + case CG_DRAW_ACTIVE_FRAME: + CG_DrawActiveFrame( arg0, arg1, arg2 ); + return 0; + case CG_CROSSHAIR_PLAYER: + return CG_CrosshairPlayer(); + case CG_LAST_ATTACKER: + return CG_LastAttacker(); + case CG_KEY_EVENT: + CG_KeyEvent(arg0, arg1); + return 0; + case CG_MOUSE_EVENT: +#ifdef MISSIONPACK + cgDC.cursorx = cgs.cursorX; + cgDC.cursory = cgs.cursorY; +#endif + CG_MouseEvent(arg0, arg1); + return 0; + case CG_EVENT_HANDLING: + CG_EventHandling(arg0); + return 0; + default: + CG_Error( "vmMain: unknown command %i", command ); + break; + } + return -1; +} + + +cg_t cg; +cgs_t cgs; +centity_t cg_entities[MAX_GENTITIES]; +weaponInfo_t cg_weapons[MAX_WEAPONS]; +itemInfo_t cg_items[MAX_ITEMS]; + + +vmCvar_t cg_railTrailTime; +vmCvar_t cg_centertime; +vmCvar_t cg_runpitch; +vmCvar_t cg_runroll; +vmCvar_t cg_bobup; +vmCvar_t cg_bobpitch; +vmCvar_t cg_bobroll; +vmCvar_t cg_swingSpeed; +vmCvar_t cg_shadows; +vmCvar_t cg_gibs; +vmCvar_t cg_drawTimer; +vmCvar_t cg_drawFPS; +vmCvar_t cg_drawSnapshot; +vmCvar_t cg_draw3dIcons; +vmCvar_t cg_drawIcons; +vmCvar_t cg_drawAmmoWarning; +vmCvar_t cg_drawCrosshair; +vmCvar_t cg_drawCrosshairNames; +vmCvar_t cg_drawRewards; +vmCvar_t cg_crosshairSize; +vmCvar_t cg_crosshairX; +vmCvar_t cg_crosshairY; +vmCvar_t cg_crosshairHealth; +vmCvar_t cg_draw2D; +vmCvar_t cg_drawStatus; +vmCvar_t cg_animSpeed; +vmCvar_t cg_debugAnim; +vmCvar_t cg_debugPosition; +vmCvar_t cg_debugEvents; +vmCvar_t cg_errorDecay; +vmCvar_t cg_nopredict; +vmCvar_t cg_noPlayerAnims; +vmCvar_t cg_showmiss; +vmCvar_t cg_footsteps; +vmCvar_t cg_addMarks; +vmCvar_t cg_brassTime; +vmCvar_t cg_viewsize; +vmCvar_t cg_drawGun; +vmCvar_t cg_gun_frame; +vmCvar_t cg_gun_x; +vmCvar_t cg_gun_y; +vmCvar_t cg_gun_z; +vmCvar_t cg_tracerChance; +vmCvar_t cg_tracerWidth; +vmCvar_t cg_tracerLength; +vmCvar_t cg_autoswitch; +vmCvar_t cg_ignore; +vmCvar_t cg_simpleItems; +vmCvar_t cg_fov; +vmCvar_t cg_zoomFov; +vmCvar_t cg_thirdPerson; +vmCvar_t cg_thirdPersonRange; +vmCvar_t cg_thirdPersonAngle; +vmCvar_t cg_lagometer; +vmCvar_t cg_drawAttacker; +vmCvar_t cg_synchronousClients; +vmCvar_t cg_teamChatTime; +vmCvar_t cg_teamChatHeight; +vmCvar_t cg_stats; +vmCvar_t cg_buildScript; +vmCvar_t cg_forceModel; +vmCvar_t cg_paused; +vmCvar_t cg_blood; +vmCvar_t cg_predictItems; +vmCvar_t cg_deferPlayers; +vmCvar_t cg_drawTeamOverlay; +vmCvar_t cg_teamOverlayUserinfo; +vmCvar_t cg_drawFriend; +vmCvar_t cg_teamChatsOnly; +vmCvar_t cg_noVoiceChats; +vmCvar_t cg_noVoiceText; +vmCvar_t cg_hudFiles; +vmCvar_t cg_scorePlum; +vmCvar_t cg_smoothClients; +vmCvar_t pmove_fixed; +//vmCvar_t cg_pmove_fixed; +vmCvar_t pmove_msec; +vmCvar_t cg_pmove_msec; +vmCvar_t cg_cameraMode; +vmCvar_t cg_cameraOrbit; +vmCvar_t cg_cameraOrbitDelay; +vmCvar_t cg_timescaleFadeEnd; +vmCvar_t cg_timescaleFadeSpeed; +vmCvar_t cg_timescale; +vmCvar_t cg_smallFont; +vmCvar_t cg_bigFont; +vmCvar_t cg_noTaunt; +vmCvar_t cg_noProjectileTrail; +vmCvar_t cg_oldRail; +vmCvar_t cg_oldRocket; +vmCvar_t cg_oldPlasma; +vmCvar_t cg_trueLightning; + +#ifdef MISSIONPACK +vmCvar_t cg_redTeamName; +vmCvar_t cg_blueTeamName; +vmCvar_t cg_currentSelectedPlayer; +vmCvar_t cg_currentSelectedPlayerName; +vmCvar_t cg_singlePlayer; +vmCvar_t cg_enableDust; +vmCvar_t cg_enableBreath; +vmCvar_t cg_singlePlayerActive; +vmCvar_t cg_recordSPDemo; +vmCvar_t cg_recordSPDemoName; +vmCvar_t cg_obeliskRespawnDelay; +#endif + +typedef struct { + vmCvar_t *vmCvar; + char *cvarName; + char *defaultString; + int cvarFlags; +} cvarTable_t; + +static cvarTable_t cvarTable[] = { + { &cg_ignore, "cg_ignore", "0", 0 }, // used for debugging + { &cg_autoswitch, "cg_autoswitch", "1", CVAR_ARCHIVE }, + { &cg_drawGun, "cg_drawGun", "1", CVAR_ARCHIVE }, + { &cg_zoomFov, "cg_zoomfov", "22.5", CVAR_ARCHIVE }, + { &cg_fov, "cg_fov", "90", CVAR_ARCHIVE }, + { &cg_viewsize, "cg_viewsize", "100", CVAR_ARCHIVE }, + { &cg_shadows, "cg_shadows", "1", CVAR_ARCHIVE }, + { &cg_gibs, "cg_gibs", "1", CVAR_ARCHIVE }, + { &cg_draw2D, "cg_draw2D", "1", CVAR_ARCHIVE }, + { &cg_drawStatus, "cg_drawStatus", "1", CVAR_ARCHIVE }, + { &cg_drawTimer, "cg_drawTimer", "0", CVAR_ARCHIVE }, + { &cg_drawFPS, "cg_drawFPS", "0", CVAR_ARCHIVE }, + { &cg_drawSnapshot, "cg_drawSnapshot", "0", CVAR_ARCHIVE }, + { &cg_draw3dIcons, "cg_draw3dIcons", "1", CVAR_ARCHIVE }, + { &cg_drawIcons, "cg_drawIcons", "1", CVAR_ARCHIVE }, + { &cg_drawAmmoWarning, "cg_drawAmmoWarning", "1", CVAR_ARCHIVE }, + { &cg_drawAttacker, "cg_drawAttacker", "1", CVAR_ARCHIVE }, + { &cg_drawCrosshair, "cg_drawCrosshair", "4", CVAR_ARCHIVE }, + { &cg_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE }, + { &cg_drawRewards, "cg_drawRewards", "1", CVAR_ARCHIVE }, + { &cg_crosshairSize, "cg_crosshairSize", "24", CVAR_ARCHIVE }, + { &cg_crosshairHealth, "cg_crosshairHealth", "1", CVAR_ARCHIVE }, + { &cg_crosshairX, "cg_crosshairX", "0", CVAR_ARCHIVE }, + { &cg_crosshairY, "cg_crosshairY", "0", CVAR_ARCHIVE }, + { &cg_brassTime, "cg_brassTime", "2500", CVAR_ARCHIVE }, + { &cg_simpleItems, "cg_simpleItems", "0", CVAR_ARCHIVE }, + { &cg_addMarks, "cg_marks", "1", CVAR_ARCHIVE }, + { &cg_lagometer, "cg_lagometer", "1", CVAR_ARCHIVE }, + { &cg_railTrailTime, "cg_railTrailTime", "400", CVAR_ARCHIVE }, + { &cg_gun_x, "cg_gunX", "0", CVAR_CHEAT }, + { &cg_gun_y, "cg_gunY", "0", CVAR_CHEAT }, + { &cg_gun_z, "cg_gunZ", "0", CVAR_CHEAT }, + { &cg_centertime, "cg_centertime", "3", CVAR_CHEAT }, + { &cg_runpitch, "cg_runpitch", "0.002", CVAR_ARCHIVE}, + { &cg_runroll, "cg_runroll", "0.005", CVAR_ARCHIVE }, + { &cg_bobup , "cg_bobup", "0.005", CVAR_CHEAT }, + { &cg_bobpitch, "cg_bobpitch", "0.002", CVAR_ARCHIVE }, + { &cg_bobroll, "cg_bobroll", "0.002", CVAR_ARCHIVE }, + { &cg_swingSpeed, "cg_swingSpeed", "0.3", CVAR_CHEAT }, + { &cg_animSpeed, "cg_animspeed", "1", CVAR_CHEAT }, + { &cg_debugAnim, "cg_debuganim", "0", CVAR_CHEAT }, + { &cg_debugPosition, "cg_debugposition", "0", CVAR_CHEAT }, + { &cg_debugEvents, "cg_debugevents", "0", CVAR_CHEAT }, + { &cg_errorDecay, "cg_errordecay", "100", 0 }, + { &cg_nopredict, "cg_nopredict", "0", 0 }, + { &cg_noPlayerAnims, "cg_noplayeranims", "0", CVAR_CHEAT }, + { &cg_showmiss, "cg_showmiss", "0", 0 }, + { &cg_footsteps, "cg_footsteps", "1", CVAR_CHEAT }, + { &cg_tracerChance, "cg_tracerchance", "0.4", CVAR_CHEAT }, + { &cg_tracerWidth, "cg_tracerwidth", "1", CVAR_CHEAT }, + { &cg_tracerLength, "cg_tracerlength", "100", CVAR_CHEAT }, + { &cg_thirdPersonRange, "cg_thirdPersonRange", "40", CVAR_CHEAT }, + { &cg_thirdPersonAngle, "cg_thirdPersonAngle", "0", CVAR_CHEAT }, + { &cg_thirdPerson, "cg_thirdPerson", "0", 0 }, + { &cg_teamChatTime, "cg_teamChatTime", "3000", CVAR_ARCHIVE }, + { &cg_teamChatHeight, "cg_teamChatHeight", "0", CVAR_ARCHIVE }, + { &cg_forceModel, "cg_forceModel", "0", CVAR_ARCHIVE }, + { &cg_predictItems, "cg_predictItems", "1", CVAR_ARCHIVE }, +#ifdef MISSIONPACK + { &cg_deferPlayers, "cg_deferPlayers", "0", CVAR_ARCHIVE }, +#else + { &cg_deferPlayers, "cg_deferPlayers", "1", CVAR_ARCHIVE }, +#endif + { &cg_drawTeamOverlay, "cg_drawTeamOverlay", "0", CVAR_ARCHIVE }, + { &cg_teamOverlayUserinfo, "teamoverlay", "0", CVAR_ROM | CVAR_USERINFO }, + { &cg_stats, "cg_stats", "0", 0 }, + { &cg_drawFriend, "cg_drawFriend", "1", CVAR_ARCHIVE }, + { &cg_teamChatsOnly, "cg_teamChatsOnly", "0", CVAR_ARCHIVE }, + { &cg_noVoiceChats, "cg_noVoiceChats", "0", CVAR_ARCHIVE }, + { &cg_noVoiceText, "cg_noVoiceText", "0", CVAR_ARCHIVE }, + // the following variables are created in other parts of the system, + // but we also reference them here + { &cg_buildScript, "com_buildScript", "0", 0 }, // force loading of all possible data amd error on failures + { &cg_paused, "cl_paused", "0", CVAR_ROM }, + { &cg_blood, "com_blood", "1", CVAR_ARCHIVE }, + { &cg_synchronousClients, "g_synchronousClients", "0", 0 }, // communicated by systeminfo +#ifdef MISSIONPACK + { &cg_redTeamName, "g_redteam", DEFAULT_REDTEAM_NAME, CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO }, + { &cg_blueTeamName, "g_blueteam", DEFAULT_BLUETEAM_NAME, CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO }, + { &cg_currentSelectedPlayer, "cg_currentSelectedPlayer", "0", CVAR_ARCHIVE}, + { &cg_currentSelectedPlayerName, "cg_currentSelectedPlayerName", "", CVAR_ARCHIVE}, + { &cg_singlePlayer, "ui_singlePlayerActive", "0", CVAR_USERINFO}, + { &cg_enableDust, "g_enableDust", "0", CVAR_SERVERINFO}, + { &cg_enableBreath, "g_enableBreath", "0", CVAR_SERVERINFO}, + { &cg_singlePlayerActive, "ui_singlePlayerActive", "0", CVAR_USERINFO}, + { &cg_recordSPDemo, "ui_recordSPDemo", "0", CVAR_ARCHIVE}, + { &cg_recordSPDemoName, "ui_recordSPDemoName", "", CVAR_ARCHIVE}, + { &cg_obeliskRespawnDelay, "g_obeliskRespawnDelay", "10", CVAR_SERVERINFO}, + { &cg_hudFiles, "cg_hudFiles", "ui/hud.txt", CVAR_ARCHIVE}, +#endif + { &cg_cameraOrbit, "cg_cameraOrbit", "0", CVAR_CHEAT}, + { &cg_cameraOrbitDelay, "cg_cameraOrbitDelay", "50", CVAR_ARCHIVE}, + { &cg_timescaleFadeEnd, "cg_timescaleFadeEnd", "1", 0}, + { &cg_timescaleFadeSpeed, "cg_timescaleFadeSpeed", "0", 0}, + { &cg_timescale, "timescale", "1", 0}, + { &cg_scorePlum, "cg_scorePlums", "1", CVAR_USERINFO | CVAR_ARCHIVE}, + { &cg_smoothClients, "cg_smoothClients", "0", CVAR_USERINFO | CVAR_ARCHIVE}, + { &cg_cameraMode, "com_cameraMode", "0", CVAR_CHEAT}, + + { &pmove_fixed, "pmove_fixed", "0", 0}, + { &pmove_msec, "pmove_msec", "8", 0}, + { &cg_noTaunt, "cg_noTaunt", "0", CVAR_ARCHIVE}, + { &cg_noProjectileTrail, "cg_noProjectileTrail", "0", CVAR_ARCHIVE}, + { &cg_smallFont, "ui_smallFont", "0.25", CVAR_ARCHIVE}, + { &cg_bigFont, "ui_bigFont", "0.4", CVAR_ARCHIVE}, + { &cg_oldRail, "cg_oldRail", "1", CVAR_ARCHIVE}, + { &cg_oldRocket, "cg_oldRocket", "1", CVAR_ARCHIVE}, + { &cg_oldPlasma, "cg_oldPlasma", "1", CVAR_ARCHIVE}, + { &cg_trueLightning, "cg_trueLightning", "0.0", CVAR_ARCHIVE} +// { &cg_pmove_fixed, "cg_pmove_fixed", "0", CVAR_USERINFO | CVAR_ARCHIVE } +}; + +static int cvarTableSize = sizeof( cvarTable ) / sizeof( cvarTable[0] ); + +/* +================= +CG_RegisterCvars +================= +*/ +void CG_RegisterCvars( void ) { + int i; + cvarTable_t *cv; + char var[MAX_TOKEN_CHARS]; + + for ( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ ) { + trap_Cvar_Register( cv->vmCvar, cv->cvarName, + cv->defaultString, cv->cvarFlags ); + } + + // see if we are also running the server on this machine + trap_Cvar_VariableStringBuffer( "sv_running", var, sizeof( var ) ); + cgs.localServer = atoi( var ); + + forceModelModificationCount = cg_forceModel.modificationCount; + + trap_Cvar_Register(NULL, "model", DEFAULT_MODEL, CVAR_USERINFO | CVAR_ARCHIVE ); + trap_Cvar_Register(NULL, "headmodel", DEFAULT_MODEL, CVAR_USERINFO | CVAR_ARCHIVE ); + trap_Cvar_Register(NULL, "team_model", DEFAULT_TEAM_MODEL, CVAR_USERINFO | CVAR_ARCHIVE ); + trap_Cvar_Register(NULL, "team_headmodel", DEFAULT_TEAM_HEAD, CVAR_USERINFO | CVAR_ARCHIVE ); +} + +/* +=================== +CG_ForceModelChange +=================== +*/ +static void CG_ForceModelChange( void ) { + int i; + + for (i=0 ; ivmCvar ); + } + + // check for modications here + + // If team overlay is on, ask for updates from the server. If its off, + // let the server know so we don't receive it + if ( drawTeamOverlayModificationCount != cg_drawTeamOverlay.modificationCount ) { + drawTeamOverlayModificationCount = cg_drawTeamOverlay.modificationCount; + + if ( cg_drawTeamOverlay.integer > 0 ) { + trap_Cvar_Set( "teamoverlay", "1" ); + } else { + trap_Cvar_Set( "teamoverlay", "0" ); + } + } + + // if force model changed + if ( forceModelModificationCount != cg_forceModel.modificationCount ) { + forceModelModificationCount = cg_forceModel.modificationCount; + CG_ForceModelChange(); + } +} + +int CG_CrosshairPlayer( void ) { + if ( cg.time > ( cg.crosshairClientTime + 1000 ) ) { + return -1; + } + return cg.crosshairClientNum; +} + +int CG_LastAttacker( void ) { + if ( !cg.attackerTime ) { + return -1; + } + return cg.snap->ps.persistant[PERS_ATTACKER]; +} + +void QDECL CG_Printf( const char *msg, ... ) { + va_list argptr; + char text[1024]; + + va_start (argptr, msg); + Q_vsnprintf (text, sizeof(text), msg, argptr); + va_end (argptr); + + trap_Print( text ); +} + +void QDECL CG_Error( const char *msg, ... ) { + va_list argptr; + char text[1024]; + + va_start (argptr, msg); + Q_vsnprintf (text, sizeof(text), msg, argptr); + va_end (argptr); + + trap_Error( text ); +} + +void QDECL Com_Error( int level, const char *error, ... ) { + va_list argptr; + char text[1024]; + + va_start (argptr, error); + Q_vsnprintf (text, sizeof(text), error, argptr); + va_end (argptr); + + CG_Error( "%s", text); +} + +void QDECL Com_Printf( const char *msg, ... ) { + va_list argptr; + char text[1024]; + + va_start (argptr, msg); + Q_vsnprintf (text, sizeof(text), msg, argptr); + va_end (argptr); + + CG_Printf ("%s", text); +} + +/* +================ +CG_Argv +================ +*/ +const char *CG_Argv( int arg ) { + static char buffer[MAX_STRING_CHARS]; + + trap_Argv( arg, buffer, sizeof( buffer ) ); + + return buffer; +} + + +//======================================================================== + +/* +================= +CG_RegisterItemSounds + +The server says this item is used on this level +================= +*/ +static void CG_RegisterItemSounds( int itemNum ) { + gitem_t *item; + char data[MAX_QPATH]; + char *s, *start; + int len; + + item = &bg_itemlist[ itemNum ]; + + if( item->pickup_sound ) { + trap_S_RegisterSound( item->pickup_sound, qfalse ); + } + + // parse the space seperated precache string for other media + s = item->sounds; + if (!s || !s[0]) + return; + + while (*s) { + start = s; + while (*s && *s != ' ') { + s++; + } + + len = s-start; + if (len >= MAX_QPATH || len < 5) { + CG_Error( "PrecacheItem: %s has bad precache string", + item->classname); + return; + } + memcpy (data, start, len); + data[len] = 0; + if ( *s ) { + s++; + } + + if ( !strcmp(data+len-3, "wav" )) { + trap_S_RegisterSound( data, qfalse ); + } + } +} + + +/* +================= +CG_RegisterSounds + +called during a precache command +================= +*/ +static void CG_RegisterSounds( void ) { + int i; + char items[MAX_ITEMS+1]; + char name[MAX_QPATH]; + const char *soundName; + + // voice commands +#ifdef MISSIONPACK + CG_LoadVoiceChats(); +#endif + + cgs.media.oneMinuteSound = trap_S_RegisterSound( "sound/feedback/1_minute.wav", qtrue ); + cgs.media.fiveMinuteSound = trap_S_RegisterSound( "sound/feedback/5_minute.wav", qtrue ); + cgs.media.suddenDeathSound = trap_S_RegisterSound( "sound/feedback/sudden_death.wav", qtrue ); + cgs.media.oneFragSound = trap_S_RegisterSound( "sound/feedback/1_frag.wav", qtrue ); + cgs.media.twoFragSound = trap_S_RegisterSound( "sound/feedback/2_frags.wav", qtrue ); + cgs.media.threeFragSound = trap_S_RegisterSound( "sound/feedback/3_frags.wav", qtrue ); + cgs.media.count3Sound = trap_S_RegisterSound( "sound/feedback/three.wav", qtrue ); + cgs.media.count2Sound = trap_S_RegisterSound( "sound/feedback/two.wav", qtrue ); + cgs.media.count1Sound = trap_S_RegisterSound( "sound/feedback/one.wav", qtrue ); + cgs.media.countFightSound = trap_S_RegisterSound( "sound/feedback/fight.wav", qtrue ); + cgs.media.countPrepareSound = trap_S_RegisterSound( "sound/feedback/prepare.wav", qtrue ); +#ifdef MISSIONPACK + cgs.media.countPrepareTeamSound = trap_S_RegisterSound( "sound/feedback/prepare_team.wav", qtrue ); +#endif + + if ( cgs.gametype >= GT_TEAM || cg_buildScript.integer ) { + + cgs.media.captureAwardSound = trap_S_RegisterSound( "sound/teamplay/flagcapture_yourteam.wav", qtrue ); + cgs.media.redLeadsSound = trap_S_RegisterSound( "sound/feedback/redleads.wav", qtrue ); + cgs.media.blueLeadsSound = trap_S_RegisterSound( "sound/feedback/blueleads.wav", qtrue ); + cgs.media.teamsTiedSound = trap_S_RegisterSound( "sound/feedback/teamstied.wav", qtrue ); + cgs.media.hitTeamSound = trap_S_RegisterSound( "sound/feedback/hit_teammate.wav", qtrue ); + + cgs.media.redScoredSound = trap_S_RegisterSound( "sound/teamplay/voc_red_scores.wav", qtrue ); + cgs.media.blueScoredSound = trap_S_RegisterSound( "sound/teamplay/voc_blue_scores.wav", qtrue ); + + cgs.media.captureYourTeamSound = trap_S_RegisterSound( "sound/teamplay/flagcapture_yourteam.wav", qtrue ); + cgs.media.captureOpponentSound = trap_S_RegisterSound( "sound/teamplay/flagcapture_opponent.wav", qtrue ); + + cgs.media.returnYourTeamSound = trap_S_RegisterSound( "sound/teamplay/flagreturn_yourteam.wav", qtrue ); + cgs.media.returnOpponentSound = trap_S_RegisterSound( "sound/teamplay/flagreturn_opponent.wav", qtrue ); + + cgs.media.takenYourTeamSound = trap_S_RegisterSound( "sound/teamplay/flagtaken_yourteam.wav", qtrue ); + cgs.media.takenOpponentSound = trap_S_RegisterSound( "sound/teamplay/flagtaken_opponent.wav", qtrue ); + + if ( cgs.gametype == GT_CTF || cg_buildScript.integer ) { + cgs.media.redFlagReturnedSound = trap_S_RegisterSound( "sound/teamplay/voc_red_returned.wav", qtrue ); + cgs.media.blueFlagReturnedSound = trap_S_RegisterSound( "sound/teamplay/voc_blue_returned.wav", qtrue ); + cgs.media.enemyTookYourFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_enemy_flag.wav", qtrue ); + cgs.media.yourTeamTookEnemyFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_team_flag.wav", qtrue ); + } + +#ifdef MISSIONPACK + if ( cgs.gametype == GT_1FCTF || cg_buildScript.integer ) { + // FIXME: get a replacement for this sound ? + cgs.media.neutralFlagReturnedSound = trap_S_RegisterSound( "sound/teamplay/flagreturn_opponent.wav", qtrue ); + cgs.media.yourTeamTookTheFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_team_1flag.wav", qtrue ); + cgs.media.enemyTookTheFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_enemy_1flag.wav", qtrue ); + } + + if ( cgs.gametype == GT_1FCTF || cgs.gametype == GT_CTF || cg_buildScript.integer ) { + cgs.media.youHaveFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_you_flag.wav", qtrue ); + cgs.media.holyShitSound = trap_S_RegisterSound("sound/feedback/voc_holyshit.wav", qtrue); + } + + if ( cgs.gametype == GT_OBELISK || cg_buildScript.integer ) { + cgs.media.yourBaseIsUnderAttackSound = trap_S_RegisterSound( "sound/teamplay/voc_base_attack.wav", qtrue ); + } +#else + cgs.media.youHaveFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_you_flag.wav", qtrue ); + cgs.media.holyShitSound = trap_S_RegisterSound("sound/feedback/voc_holyshit.wav", qtrue); + cgs.media.neutralFlagReturnedSound = trap_S_RegisterSound( "sound/teamplay/flagreturn_opponent.wav", qtrue ); + cgs.media.yourTeamTookTheFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_team_1flag.wav", qtrue ); + cgs.media.enemyTookTheFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_enemy_1flag.wav", qtrue ); +#endif + } + + cgs.media.tracerSound = trap_S_RegisterSound( "sound/weapons/machinegun/buletby1.wav", qfalse ); + cgs.media.selectSound = trap_S_RegisterSound( "sound/weapons/change.wav", qfalse ); + cgs.media.wearOffSound = trap_S_RegisterSound( "sound/items/wearoff.wav", qfalse ); + cgs.media.useNothingSound = trap_S_RegisterSound( "sound/items/use_nothing.wav", qfalse ); + cgs.media.gibSound = trap_S_RegisterSound( "sound/player/gibsplt1.wav", qfalse ); + cgs.media.gibBounce1Sound = trap_S_RegisterSound( "sound/player/gibimp1.wav", qfalse ); + cgs.media.gibBounce2Sound = trap_S_RegisterSound( "sound/player/gibimp2.wav", qfalse ); + cgs.media.gibBounce3Sound = trap_S_RegisterSound( "sound/player/gibimp3.wav", qfalse ); + +#ifdef MISSIONPACK + cgs.media.useInvulnerabilitySound = trap_S_RegisterSound( "sound/items/invul_activate.wav", qfalse ); + cgs.media.invulnerabilityImpactSound1 = trap_S_RegisterSound( "sound/items/invul_impact_01.wav", qfalse ); + cgs.media.invulnerabilityImpactSound2 = trap_S_RegisterSound( "sound/items/invul_impact_02.wav", qfalse ); + cgs.media.invulnerabilityImpactSound3 = trap_S_RegisterSound( "sound/items/invul_impact_03.wav", qfalse ); + cgs.media.invulnerabilityJuicedSound = trap_S_RegisterSound( "sound/items/invul_juiced.wav", qfalse ); + cgs.media.obeliskHitSound1 = trap_S_RegisterSound( "sound/items/obelisk_hit_01.wav", qfalse ); + cgs.media.obeliskHitSound2 = trap_S_RegisterSound( "sound/items/obelisk_hit_02.wav", qfalse ); + cgs.media.obeliskHitSound3 = trap_S_RegisterSound( "sound/items/obelisk_hit_03.wav", qfalse ); + cgs.media.obeliskRespawnSound = trap_S_RegisterSound( "sound/items/obelisk_respawn.wav", qfalse ); + + cgs.media.ammoregenSound = trap_S_RegisterSound("sound/items/cl_ammoregen.wav", qfalse); + cgs.media.doublerSound = trap_S_RegisterSound("sound/items/cl_doubler.wav", qfalse); + cgs.media.guardSound = trap_S_RegisterSound("sound/items/cl_guard.wav", qfalse); + cgs.media.scoutSound = trap_S_RegisterSound("sound/items/cl_scout.wav", qfalse); +#endif + + cgs.media.teleInSound = trap_S_RegisterSound( "sound/world/telein.wav", qfalse ); + cgs.media.teleOutSound = trap_S_RegisterSound( "sound/world/teleout.wav", qfalse ); + cgs.media.respawnSound = trap_S_RegisterSound( "sound/items/respawn1.wav", qfalse ); + + cgs.media.noAmmoSound = trap_S_RegisterSound( "sound/weapons/noammo.wav", qfalse ); + + cgs.media.talkSound = trap_S_RegisterSound( "sound/player/talk.wav", qfalse ); + cgs.media.landSound = trap_S_RegisterSound( "sound/player/land1.wav", qfalse); + + cgs.media.hitSound = trap_S_RegisterSound( "sound/feedback/hit.wav", qfalse ); +#ifdef MISSIONPACK + cgs.media.hitSoundHighArmor = trap_S_RegisterSound( "sound/feedback/hithi.wav", qfalse ); + cgs.media.hitSoundLowArmor = trap_S_RegisterSound( "sound/feedback/hitlo.wav", qfalse ); +#endif + + cgs.media.impressiveSound = trap_S_RegisterSound( "sound/feedback/impressive.wav", qtrue ); + cgs.media.excellentSound = trap_S_RegisterSound( "sound/feedback/excellent.wav", qtrue ); + cgs.media.deniedSound = trap_S_RegisterSound( "sound/feedback/denied.wav", qtrue ); + cgs.media.humiliationSound = trap_S_RegisterSound( "sound/feedback/humiliation.wav", qtrue ); + cgs.media.assistSound = trap_S_RegisterSound( "sound/feedback/assist.wav", qtrue ); + cgs.media.defendSound = trap_S_RegisterSound( "sound/feedback/defense.wav", qtrue ); +#ifdef MISSIONPACK + cgs.media.firstImpressiveSound = trap_S_RegisterSound( "sound/feedback/first_impressive.wav", qtrue ); + cgs.media.firstExcellentSound = trap_S_RegisterSound( "sound/feedback/first_excellent.wav", qtrue ); + cgs.media.firstHumiliationSound = trap_S_RegisterSound( "sound/feedback/first_gauntlet.wav", qtrue ); +#endif + + cgs.media.takenLeadSound = trap_S_RegisterSound( "sound/feedback/takenlead.wav", qtrue); + cgs.media.tiedLeadSound = trap_S_RegisterSound( "sound/feedback/tiedlead.wav", qtrue); + cgs.media.lostLeadSound = trap_S_RegisterSound( "sound/feedback/lostlead.wav", qtrue); + +#ifdef MISSIONPACK + cgs.media.voteNow = trap_S_RegisterSound( "sound/feedback/vote_now.wav", qtrue); + cgs.media.votePassed = trap_S_RegisterSound( "sound/feedback/vote_passed.wav", qtrue); + cgs.media.voteFailed = trap_S_RegisterSound( "sound/feedback/vote_failed.wav", qtrue); +#endif + + cgs.media.watrInSound = trap_S_RegisterSound( "sound/player/watr_in.wav", qfalse); + cgs.media.watrOutSound = trap_S_RegisterSound( "sound/player/watr_out.wav", qfalse); + cgs.media.watrUnSound = trap_S_RegisterSound( "sound/player/watr_un.wav", qfalse); + + cgs.media.jumpPadSound = trap_S_RegisterSound ("sound/world/jumppad.wav", qfalse ); + + for (i=0 ; i<4 ; i++) { + Com_sprintf (name, sizeof(name), "sound/player/footsteps/step%i.wav", i+1); + cgs.media.footsteps[FOOTSTEP_NORMAL][i] = trap_S_RegisterSound (name, qfalse); + + Com_sprintf (name, sizeof(name), "sound/player/footsteps/boot%i.wav", i+1); + cgs.media.footsteps[FOOTSTEP_BOOT][i] = trap_S_RegisterSound (name, qfalse); + + Com_sprintf (name, sizeof(name), "sound/player/footsteps/flesh%i.wav", i+1); + cgs.media.footsteps[FOOTSTEP_FLESH][i] = trap_S_RegisterSound (name, qfalse); + + Com_sprintf (name, sizeof(name), "sound/player/footsteps/mech%i.wav", i+1); + cgs.media.footsteps[FOOTSTEP_MECH][i] = trap_S_RegisterSound (name, qfalse); + + Com_sprintf (name, sizeof(name), "sound/player/footsteps/energy%i.wav", i+1); + cgs.media.footsteps[FOOTSTEP_ENERGY][i] = trap_S_RegisterSound (name, qfalse); + + Com_sprintf (name, sizeof(name), "sound/player/footsteps/splash%i.wav", i+1); + cgs.media.footsteps[FOOTSTEP_SPLASH][i] = trap_S_RegisterSound (name, qfalse); + + Com_sprintf (name, sizeof(name), "sound/player/footsteps/clank%i.wav", i+1); + cgs.media.footsteps[FOOTSTEP_METAL][i] = trap_S_RegisterSound (name, qfalse); + } + + // only register the items that the server says we need + Q_strncpyz(items, CG_ConfigString(CS_ITEMS), sizeof(items)); + + for ( i = 1 ; i < bg_numItems ; i++ ) { +// if ( items[ i ] == '1' || cg_buildScript.integer ) { + CG_RegisterItemSounds( i ); +// } + } + + for ( i = 1 ; i < MAX_SOUNDS ; i++ ) { + soundName = CG_ConfigString( CS_SOUNDS+i ); + if ( !soundName[0] ) { + break; + } + if ( soundName[0] == '*' ) { + continue; // custom sound + } + cgs.gameSounds[i] = trap_S_RegisterSound( soundName, qfalse ); + } + + // FIXME: only needed with item + cgs.media.flightSound = trap_S_RegisterSound( "sound/items/flight.wav", qfalse ); + cgs.media.medkitSound = trap_S_RegisterSound ("sound/items/use_medkit.wav", qfalse); + cgs.media.quadSound = trap_S_RegisterSound("sound/items/damage3.wav", qfalse); + cgs.media.sfx_ric1 = trap_S_RegisterSound ("sound/weapons/machinegun/ric1.wav", qfalse); + cgs.media.sfx_ric2 = trap_S_RegisterSound ("sound/weapons/machinegun/ric2.wav", qfalse); + cgs.media.sfx_ric3 = trap_S_RegisterSound ("sound/weapons/machinegun/ric3.wav", qfalse); + cgs.media.sfx_railg = trap_S_RegisterSound ("sound/weapons/railgun/railgf1a.wav", qfalse); + cgs.media.sfx_rockexp = trap_S_RegisterSound ("sound/weapons/rocket/rocklx1a.wav", qfalse); + cgs.media.sfx_plasmaexp = trap_S_RegisterSound ("sound/weapons/plasma/plasmx1a.wav", qfalse); +#ifdef MISSIONPACK + cgs.media.sfx_proxexp = trap_S_RegisterSound( "sound/weapons/proxmine/wstbexpl.wav" , qfalse); + cgs.media.sfx_nghit = trap_S_RegisterSound( "sound/weapons/nailgun/wnalimpd.wav" , qfalse); + cgs.media.sfx_nghitflesh = trap_S_RegisterSound( "sound/weapons/nailgun/wnalimpl.wav" , qfalse); + cgs.media.sfx_nghitmetal = trap_S_RegisterSound( "sound/weapons/nailgun/wnalimpm.wav", qfalse ); + cgs.media.sfx_chghit = trap_S_RegisterSound( "sound/weapons/vulcan/wvulimpd.wav", qfalse ); + cgs.media.sfx_chghitflesh = trap_S_RegisterSound( "sound/weapons/vulcan/wvulimpl.wav", qfalse ); + cgs.media.sfx_chghitmetal = trap_S_RegisterSound( "sound/weapons/vulcan/wvulimpm.wav", qfalse ); + cgs.media.weaponHoverSound = trap_S_RegisterSound( "sound/weapons/weapon_hover.wav", qfalse ); + cgs.media.kamikazeExplodeSound = trap_S_RegisterSound( "sound/items/kam_explode.wav", qfalse ); + cgs.media.kamikazeImplodeSound = trap_S_RegisterSound( "sound/items/kam_implode.wav", qfalse ); + cgs.media.kamikazeFarSound = trap_S_RegisterSound( "sound/items/kam_explode_far.wav", qfalse ); + cgs.media.winnerSound = trap_S_RegisterSound( "sound/feedback/voc_youwin.wav", qfalse ); + cgs.media.loserSound = trap_S_RegisterSound( "sound/feedback/voc_youlose.wav", qfalse ); + cgs.media.youSuckSound = trap_S_RegisterSound( "sound/misc/yousuck.wav", qfalse ); + + cgs.media.wstbimplSound = trap_S_RegisterSound("sound/weapons/proxmine/wstbimpl.wav", qfalse); + cgs.media.wstbimpmSound = trap_S_RegisterSound("sound/weapons/proxmine/wstbimpm.wav", qfalse); + cgs.media.wstbimpdSound = trap_S_RegisterSound("sound/weapons/proxmine/wstbimpd.wav", qfalse); + cgs.media.wstbactvSound = trap_S_RegisterSound("sound/weapons/proxmine/wstbactv.wav", qfalse); +#endif + + cgs.media.regenSound = trap_S_RegisterSound("sound/items/regen.wav", qfalse); + cgs.media.protectSound = trap_S_RegisterSound("sound/items/protect3.wav", qfalse); + cgs.media.n_healthSound = trap_S_RegisterSound("sound/items/n_health.wav", qfalse ); + cgs.media.hgrenb1aSound = trap_S_RegisterSound("sound/weapons/grenade/hgrenb1a.wav", qfalse); + cgs.media.hgrenb2aSound = trap_S_RegisterSound("sound/weapons/grenade/hgrenb2a.wav", qfalse); + +#ifdef MISSIONPACK + trap_S_RegisterSound("sound/player/james/death1.wav", qfalse ); + trap_S_RegisterSound("sound/player/james/death2.wav", qfalse ); + trap_S_RegisterSound("sound/player/james/death3.wav", qfalse ); + trap_S_RegisterSound("sound/player/james/jump1.wav", qfalse ); + trap_S_RegisterSound("sound/player/james/pain25_1.wav", qfalse ); + trap_S_RegisterSound("sound/player/james/pain75_1.wav", qfalse ); + trap_S_RegisterSound("sound/player/james/pain100_1.wav", qfalse ); + trap_S_RegisterSound("sound/player/james/falling1.wav", qfalse ); + trap_S_RegisterSound("sound/player/james/gasp.wav", qfalse ); + trap_S_RegisterSound("sound/player/james/drown.wav", qfalse ); + trap_S_RegisterSound("sound/player/james/fall1.wav", qfalse ); + trap_S_RegisterSound("sound/player/james/taunt.wav", qfalse ); + + trap_S_RegisterSound("sound/player/janet/death1.wav", qfalse ); + trap_S_RegisterSound("sound/player/janet/death2.wav", qfalse ); + trap_S_RegisterSound("sound/player/janet/death3.wav", qfalse ); + trap_S_RegisterSound("sound/player/janet/jump1.wav", qfalse ); + trap_S_RegisterSound("sound/player/janet/pain25_1.wav", qfalse ); + trap_S_RegisterSound("sound/player/janet/pain75_1.wav", qfalse ); + trap_S_RegisterSound("sound/player/janet/pain100_1.wav", qfalse ); + trap_S_RegisterSound("sound/player/janet/falling1.wav", qfalse ); + trap_S_RegisterSound("sound/player/janet/gasp.wav", qfalse ); + trap_S_RegisterSound("sound/player/janet/drown.wav", qfalse ); + trap_S_RegisterSound("sound/player/janet/fall1.wav", qfalse ); + trap_S_RegisterSound("sound/player/janet/taunt.wav", qfalse ); +#endif + +} + + +//=================================================================================== + + +/* +================= +CG_RegisterGraphics + +This function may execute for a couple of minutes with a slow disk. +================= +*/ +static void CG_RegisterGraphics( void ) { + int i; + char items[MAX_ITEMS+1]; + static char *sb_nums[11] = { + "gfx/2d/numbers/zero_32b", + "gfx/2d/numbers/one_32b", + "gfx/2d/numbers/two_32b", + "gfx/2d/numbers/three_32b", + "gfx/2d/numbers/four_32b", + "gfx/2d/numbers/five_32b", + "gfx/2d/numbers/six_32b", + "gfx/2d/numbers/seven_32b", + "gfx/2d/numbers/eight_32b", + "gfx/2d/numbers/nine_32b", + "gfx/2d/numbers/minus_32b", + }; + + // clear any references to old media + memset( &cg.refdef, 0, sizeof( cg.refdef ) ); + trap_R_ClearScene(); + + CG_LoadingString( cgs.mapname ); + + trap_R_LoadWorldMap( cgs.mapname ); + + // precache status bar pics + CG_LoadingString( "game media" ); + + for ( i=0 ; i<11 ; i++) { + cgs.media.numberShaders[i] = trap_R_RegisterShader( sb_nums[i] ); + } + + cgs.media.botSkillShaders[0] = trap_R_RegisterShader( "menu/art/skill1.tga" ); + cgs.media.botSkillShaders[1] = trap_R_RegisterShader( "menu/art/skill2.tga" ); + cgs.media.botSkillShaders[2] = trap_R_RegisterShader( "menu/art/skill3.tga" ); + cgs.media.botSkillShaders[3] = trap_R_RegisterShader( "menu/art/skill4.tga" ); + cgs.media.botSkillShaders[4] = trap_R_RegisterShader( "menu/art/skill5.tga" ); + + cgs.media.viewBloodShader = trap_R_RegisterShader( "viewBloodBlend" ); + + cgs.media.deferShader = trap_R_RegisterShaderNoMip( "gfx/2d/defer.tga" ); + + cgs.media.scoreboardName = trap_R_RegisterShaderNoMip( "menu/tab/name.tga" ); + cgs.media.scoreboardPing = trap_R_RegisterShaderNoMip( "menu/tab/ping.tga" ); + cgs.media.scoreboardScore = trap_R_RegisterShaderNoMip( "menu/tab/score.tga" ); + cgs.media.scoreboardTime = trap_R_RegisterShaderNoMip( "menu/tab/time.tga" ); + + cgs.media.smokePuffShader = trap_R_RegisterShader( "smokePuff" ); + cgs.media.smokePuffRageProShader = trap_R_RegisterShader( "smokePuffRagePro" ); + cgs.media.shotgunSmokePuffShader = trap_R_RegisterShader( "shotgunSmokePuff" ); +#ifdef MISSIONPACK + cgs.media.nailPuffShader = trap_R_RegisterShader( "nailtrail" ); + cgs.media.blueProxMine = trap_R_RegisterModel( "models/weaphits/proxmineb.md3" ); +#endif + cgs.media.plasmaBallShader = trap_R_RegisterShader( "sprites/plasma1" ); + cgs.media.bloodTrailShader = trap_R_RegisterShader( "bloodTrail" ); + cgs.media.lagometerShader = trap_R_RegisterShader("lagometer" ); + cgs.media.connectionShader = trap_R_RegisterShader( "disconnected" ); + + cgs.media.waterBubbleShader = trap_R_RegisterShader( "waterBubble" ); + + cgs.media.tracerShader = trap_R_RegisterShader( "gfx/misc/tracer" ); + cgs.media.selectShader = trap_R_RegisterShader( "gfx/2d/select" ); + + for ( i = 0 ; i < NUM_CROSSHAIRS ; i++ ) { + cgs.media.crosshairShader[i] = trap_R_RegisterShader( va("gfx/2d/crosshair%c", 'a'+i) ); + } + + cgs.media.backTileShader = trap_R_RegisterShader( "gfx/2d/backtile" ); + cgs.media.noammoShader = trap_R_RegisterShader( "icons/noammo" ); + + // powerup shaders + cgs.media.quadShader = trap_R_RegisterShader("powerups/quad" ); + cgs.media.quadWeaponShader = trap_R_RegisterShader("powerups/quadWeapon" ); + cgs.media.battleSuitShader = trap_R_RegisterShader("powerups/battleSuit" ); + cgs.media.battleWeaponShader = trap_R_RegisterShader("powerups/battleWeapon" ); + cgs.media.invisShader = trap_R_RegisterShader("powerups/invisibility" ); + cgs.media.regenShader = trap_R_RegisterShader("powerups/regen" ); + cgs.media.hastePuffShader = trap_R_RegisterShader("hasteSmokePuff" ); + +#ifdef MISSIONPACK + if ( cgs.gametype == GT_CTF || cgs.gametype == GT_1FCTF || cgs.gametype == GT_HARVESTER || cg_buildScript.integer ) { +#else + if ( cgs.gametype == GT_CTF || cg_buildScript.integer ) { +#endif + cgs.media.redCubeModel = trap_R_RegisterModel( "models/powerups/orb/r_orb.md3" ); + cgs.media.blueCubeModel = trap_R_RegisterModel( "models/powerups/orb/b_orb.md3" ); + cgs.media.redCubeIcon = trap_R_RegisterShader( "icons/skull_red" ); + cgs.media.blueCubeIcon = trap_R_RegisterShader( "icons/skull_blue" ); + } + +#ifdef MISSIONPACK + if ( cgs.gametype == GT_CTF || cgs.gametype == GT_1FCTF || cgs.gametype == GT_HARVESTER || cg_buildScript.integer ) { +#else + if ( cgs.gametype == GT_CTF || cg_buildScript.integer ) { +#endif + cgs.media.redFlagModel = trap_R_RegisterModel( "models/flags/r_flag.md3" ); + cgs.media.blueFlagModel = trap_R_RegisterModel( "models/flags/b_flag.md3" ); + cgs.media.redFlagShader[0] = trap_R_RegisterShaderNoMip( "icons/iconf_red1" ); + cgs.media.redFlagShader[1] = trap_R_RegisterShaderNoMip( "icons/iconf_red2" ); + cgs.media.redFlagShader[2] = trap_R_RegisterShaderNoMip( "icons/iconf_red3" ); + cgs.media.blueFlagShader[0] = trap_R_RegisterShaderNoMip( "icons/iconf_blu1" ); + cgs.media.blueFlagShader[1] = trap_R_RegisterShaderNoMip( "icons/iconf_blu2" ); + cgs.media.blueFlagShader[2] = trap_R_RegisterShaderNoMip( "icons/iconf_blu3" ); +#ifdef MISSIONPACK + cgs.media.flagPoleModel = trap_R_RegisterModel( "models/flag2/flagpole.md3" ); + cgs.media.flagFlapModel = trap_R_RegisterModel( "models/flag2/flagflap3.md3" ); + + cgs.media.redFlagFlapSkin = trap_R_RegisterSkin( "models/flag2/red.skin" ); + cgs.media.blueFlagFlapSkin = trap_R_RegisterSkin( "models/flag2/blue.skin" ); + cgs.media.neutralFlagFlapSkin = trap_R_RegisterSkin( "models/flag2/white.skin" ); + + cgs.media.redFlagBaseModel = trap_R_RegisterModel( "models/mapobjects/flagbase/red_base.md3" ); + cgs.media.blueFlagBaseModel = trap_R_RegisterModel( "models/mapobjects/flagbase/blue_base.md3" ); + cgs.media.neutralFlagBaseModel = trap_R_RegisterModel( "models/mapobjects/flagbase/ntrl_base.md3" ); +#endif + } + +#ifdef MISSIONPACK + if ( cgs.gametype == GT_1FCTF || cg_buildScript.integer ) { + cgs.media.neutralFlagModel = trap_R_RegisterModel( "models/flags/n_flag.md3" ); + cgs.media.flagShader[0] = trap_R_RegisterShaderNoMip( "icons/iconf_neutral1" ); + cgs.media.flagShader[1] = trap_R_RegisterShaderNoMip( "icons/iconf_red2" ); + cgs.media.flagShader[2] = trap_R_RegisterShaderNoMip( "icons/iconf_blu2" ); + cgs.media.flagShader[3] = trap_R_RegisterShaderNoMip( "icons/iconf_neutral3" ); + } + + if ( cgs.gametype == GT_OBELISK || cg_buildScript.integer ) { + cgs.media.overloadBaseModel = trap_R_RegisterModel( "models/powerups/overload_base.md3" ); + cgs.media.overloadTargetModel = trap_R_RegisterModel( "models/powerups/overload_target.md3" ); + cgs.media.overloadLightsModel = trap_R_RegisterModel( "models/powerups/overload_lights.md3" ); + cgs.media.overloadEnergyModel = trap_R_RegisterModel( "models/powerups/overload_energy.md3" ); + } + + if ( cgs.gametype == GT_HARVESTER || cg_buildScript.integer ) { + cgs.media.harvesterModel = trap_R_RegisterModel( "models/powerups/harvester/harvester.md3" ); + cgs.media.harvesterRedSkin = trap_R_RegisterSkin( "models/powerups/harvester/red.skin" ); + cgs.media.harvesterBlueSkin = trap_R_RegisterSkin( "models/powerups/harvester/blue.skin" ); + cgs.media.harvesterNeutralModel = trap_R_RegisterModel( "models/powerups/obelisk/obelisk.md3" ); + } + + cgs.media.redKamikazeShader = trap_R_RegisterShader( "models/weaphits/kamikred" ); + cgs.media.dustPuffShader = trap_R_RegisterShader("hasteSmokePuff" ); +#endif + + if ( cgs.gametype >= GT_TEAM || cg_buildScript.integer ) { + cgs.media.friendShader = trap_R_RegisterShader( "sprites/foe" ); + cgs.media.redQuadShader = trap_R_RegisterShader("powerups/blueflag" ); + cgs.media.teamStatusBar = trap_R_RegisterShader( "gfx/2d/colorbar.tga" ); +#ifdef MISSIONPACK + cgs.media.blueKamikazeShader = trap_R_RegisterShader( "models/weaphits/kamikblu" ); +#endif + } + + cgs.media.armorModel = trap_R_RegisterModel( "models/powerups/armor/armor_yel.md3" ); + cgs.media.armorIcon = trap_R_RegisterShaderNoMip( "icons/iconr_yellow" ); + + cgs.media.machinegunBrassModel = trap_R_RegisterModel( "models/weapons2/shells/m_shell.md3" ); + cgs.media.shotgunBrassModel = trap_R_RegisterModel( "models/weapons2/shells/s_shell.md3" ); + + cgs.media.gibAbdomen = trap_R_RegisterModel( "models/gibs/abdomen.md3" ); + cgs.media.gibArm = trap_R_RegisterModel( "models/gibs/arm.md3" ); + cgs.media.gibChest = trap_R_RegisterModel( "models/gibs/chest.md3" ); + cgs.media.gibFist = trap_R_RegisterModel( "models/gibs/fist.md3" ); + cgs.media.gibFoot = trap_R_RegisterModel( "models/gibs/foot.md3" ); + cgs.media.gibForearm = trap_R_RegisterModel( "models/gibs/forearm.md3" ); + cgs.media.gibIntestine = trap_R_RegisterModel( "models/gibs/intestine.md3" ); + cgs.media.gibLeg = trap_R_RegisterModel( "models/gibs/leg.md3" ); + cgs.media.gibSkull = trap_R_RegisterModel( "models/gibs/skull.md3" ); + cgs.media.gibBrain = trap_R_RegisterModel( "models/gibs/brain.md3" ); + + cgs.media.smoke2 = trap_R_RegisterModel( "models/weapons2/shells/s_shell.md3" ); + + cgs.media.balloonShader = trap_R_RegisterShader( "sprites/balloon3" ); + + cgs.media.bloodExplosionShader = trap_R_RegisterShader( "bloodExplosion" ); + + cgs.media.bulletFlashModel = trap_R_RegisterModel("models/weaphits/bullet.md3"); + cgs.media.ringFlashModel = trap_R_RegisterModel("models/weaphits/ring02.md3"); + cgs.media.dishFlashModel = trap_R_RegisterModel("models/weaphits/boom01.md3"); +#ifdef MISSIONPACK + cgs.media.teleportEffectModel = trap_R_RegisterModel( "models/powerups/pop.md3" ); +#else + cgs.media.teleportEffectModel = trap_R_RegisterModel( "models/misc/telep.md3" ); + cgs.media.teleportEffectShader = trap_R_RegisterShader( "teleportEffect" ); +#endif +#ifdef MISSIONPACK + cgs.media.kamikazeEffectModel = trap_R_RegisterModel( "models/weaphits/kamboom2.md3" ); + cgs.media.kamikazeShockWave = trap_R_RegisterModel( "models/weaphits/kamwave.md3" ); + cgs.media.kamikazeHeadModel = trap_R_RegisterModel( "models/powerups/kamikazi.md3" ); + cgs.media.kamikazeHeadTrail = trap_R_RegisterModel( "models/powerups/trailtest.md3" ); + cgs.media.guardPowerupModel = trap_R_RegisterModel( "models/powerups/guard_player.md3" ); + cgs.media.scoutPowerupModel = trap_R_RegisterModel( "models/powerups/scout_player.md3" ); + cgs.media.doublerPowerupModel = trap_R_RegisterModel( "models/powerups/doubler_player.md3" ); + cgs.media.ammoRegenPowerupModel = trap_R_RegisterModel( "models/powerups/ammo_player.md3" ); + cgs.media.invulnerabilityImpactModel = trap_R_RegisterModel( "models/powerups/shield/impact.md3" ); + cgs.media.invulnerabilityJuicedModel = trap_R_RegisterModel( "models/powerups/shield/juicer.md3" ); + cgs.media.medkitUsageModel = trap_R_RegisterModel( "models/powerups/regen.md3" ); + cgs.media.heartShader = trap_R_RegisterShaderNoMip( "ui/assets/statusbar/selectedhealth.tga" ); + +#endif + + cgs.media.invulnerabilityPowerupModel = trap_R_RegisterModel( "models/powerups/shield/shield.md3" ); + cgs.media.medalImpressive = trap_R_RegisterShaderNoMip( "medal_impressive" ); + cgs.media.medalExcellent = trap_R_RegisterShaderNoMip( "medal_excellent" ); + cgs.media.medalGauntlet = trap_R_RegisterShaderNoMip( "medal_gauntlet" ); + cgs.media.medalDefend = trap_R_RegisterShaderNoMip( "medal_defend" ); + cgs.media.medalAssist = trap_R_RegisterShaderNoMip( "medal_assist" ); + cgs.media.medalCapture = trap_R_RegisterShaderNoMip( "medal_capture" ); + + + memset( cg_items, 0, sizeof( cg_items ) ); + memset( cg_weapons, 0, sizeof( cg_weapons ) ); + + // only register the items that the server says we need + Q_strncpyz(items, CG_ConfigString(CS_ITEMS), sizeof(items)); + + for ( i = 1 ; i < bg_numItems ; i++ ) { + if ( items[ i ] == '1' || cg_buildScript.integer ) { + CG_LoadingItem( i ); + CG_RegisterItemVisuals( i ); + } + } + + // wall marks + cgs.media.bulletMarkShader = trap_R_RegisterShader( "gfx/damage/bullet_mrk" ); + cgs.media.burnMarkShader = trap_R_RegisterShader( "gfx/damage/burn_med_mrk" ); + cgs.media.holeMarkShader = trap_R_RegisterShader( "gfx/damage/hole_lg_mrk" ); + cgs.media.energyMarkShader = trap_R_RegisterShader( "gfx/damage/plasma_mrk" ); + cgs.media.shadowMarkShader = trap_R_RegisterShader( "markShadow" ); + cgs.media.wakeMarkShader = trap_R_RegisterShader( "wake" ); + cgs.media.bloodMarkShader = trap_R_RegisterShader( "bloodMark" ); + + // register the inline models + cgs.numInlineModels = trap_CM_NumInlineModels(); + for ( i = 1 ; i < cgs.numInlineModels ; i++ ) { + char name[10]; + vec3_t mins, maxs; + int j; + + Com_sprintf( name, sizeof(name), "*%i", i ); + cgs.inlineDrawModel[i] = trap_R_RegisterModel( name ); + trap_R_ModelBounds( cgs.inlineDrawModel[i], mins, maxs ); + for ( j = 0 ; j < 3 ; j++ ) { + cgs.inlineModelMidpoints[i][j] = mins[j] + 0.5 * ( maxs[j] - mins[j] ); + } + } + + // register all the server specified models + for (i=1 ; i= MAX_CONFIGSTRINGS ) { + CG_Error( "CG_ConfigString: bad index: %i", index ); + } + return cgs.gameState.stringData + cgs.gameState.stringOffsets[ index ]; +} + +//================================================================== + +/* +====================== +CG_StartMusic + +====================== +*/ +void CG_StartMusic( void ) { + char *s; + char parm1[MAX_QPATH], parm2[MAX_QPATH]; + + // start the background music + s = (char *)CG_ConfigString( CS_MUSIC ); + Q_strncpyz( parm1, COM_Parse( &s ), sizeof( parm1 ) ); + Q_strncpyz( parm2, COM_Parse( &s ), sizeof( parm2 ) ); + + trap_S_StartBackgroundTrack( parm1, parm2 ); +} +#ifdef MISSIONPACK +char *CG_GetMenuBuffer(const char *filename) { + int len; + fileHandle_t f; + static char buf[MAX_MENUFILE]; + + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + if ( !f ) { + trap_Print( va( S_COLOR_RED "menu file not found: %s, using default\n", filename ) ); + return NULL; + } + if ( len >= MAX_MENUFILE ) { + trap_Print( va( S_COLOR_RED "menu file too large: %s is %i, max allowed is %i", filename, len, MAX_MENUFILE ) ); + trap_FS_FCloseFile( f ); + return NULL; + } + + trap_FS_Read( buf, len, f ); + buf[len] = 0; + trap_FS_FCloseFile( f ); + + return buf; +} + +// +// ============================== +// new hud stuff ( mission pack ) +// ============================== +// +qboolean CG_Asset_Parse(int handle) { + pc_token_t token; + const char *tempStr; + + if (!trap_PC_ReadToken(handle, &token)) + return qfalse; + if (Q_stricmp(token.string, "{") != 0) { + return qfalse; + } + + while ( 1 ) { + if (!trap_PC_ReadToken(handle, &token)) + return qfalse; + + if (Q_stricmp(token.string, "}") == 0) { + return qtrue; + } + + // font + if (Q_stricmp(token.string, "font") == 0) { + int pointSize; + if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle, &pointSize)) { + return qfalse; + } + cgDC.registerFont(tempStr, pointSize, &cgDC.Assets.textFont); + continue; + } + + // smallFont + if (Q_stricmp(token.string, "smallFont") == 0) { + int pointSize; + if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle, &pointSize)) { + return qfalse; + } + cgDC.registerFont(tempStr, pointSize, &cgDC.Assets.smallFont); + continue; + } + + // font + if (Q_stricmp(token.string, "bigfont") == 0) { + int pointSize; + if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle, &pointSize)) { + return qfalse; + } + cgDC.registerFont(tempStr, pointSize, &cgDC.Assets.bigFont); + continue; + } + + // gradientbar + if (Q_stricmp(token.string, "gradientbar") == 0) { + if (!PC_String_Parse(handle, &tempStr)) { + return qfalse; + } + cgDC.Assets.gradientBar = trap_R_RegisterShaderNoMip(tempStr); + continue; + } + + // enterMenuSound + if (Q_stricmp(token.string, "menuEnterSound") == 0) { + if (!PC_String_Parse(handle, &tempStr)) { + return qfalse; + } + cgDC.Assets.menuEnterSound = trap_S_RegisterSound( tempStr, qfalse ); + continue; + } + + // exitMenuSound + if (Q_stricmp(token.string, "menuExitSound") == 0) { + if (!PC_String_Parse(handle, &tempStr)) { + return qfalse; + } + cgDC.Assets.menuExitSound = trap_S_RegisterSound( tempStr, qfalse ); + continue; + } + + // itemFocusSound + if (Q_stricmp(token.string, "itemFocusSound") == 0) { + if (!PC_String_Parse(handle, &tempStr)) { + return qfalse; + } + cgDC.Assets.itemFocusSound = trap_S_RegisterSound( tempStr, qfalse ); + continue; + } + + // menuBuzzSound + if (Q_stricmp(token.string, "menuBuzzSound") == 0) { + if (!PC_String_Parse(handle, &tempStr)) { + return qfalse; + } + cgDC.Assets.menuBuzzSound = trap_S_RegisterSound( tempStr, qfalse ); + continue; + } + + if (Q_stricmp(token.string, "cursor") == 0) { + if (!PC_String_Parse(handle, &cgDC.Assets.cursorStr)) { + return qfalse; + } + cgDC.Assets.cursor = trap_R_RegisterShaderNoMip( cgDC.Assets.cursorStr); + continue; + } + + if (Q_stricmp(token.string, "fadeClamp") == 0) { + if (!PC_Float_Parse(handle, &cgDC.Assets.fadeClamp)) { + return qfalse; + } + continue; + } + + if (Q_stricmp(token.string, "fadeCycle") == 0) { + if (!PC_Int_Parse(handle, &cgDC.Assets.fadeCycle)) { + return qfalse; + } + continue; + } + + if (Q_stricmp(token.string, "fadeAmount") == 0) { + if (!PC_Float_Parse(handle, &cgDC.Assets.fadeAmount)) { + return qfalse; + } + continue; + } + + if (Q_stricmp(token.string, "shadowX") == 0) { + if (!PC_Float_Parse(handle, &cgDC.Assets.shadowX)) { + return qfalse; + } + continue; + } + + if (Q_stricmp(token.string, "shadowY") == 0) { + if (!PC_Float_Parse(handle, &cgDC.Assets.shadowY)) { + return qfalse; + } + continue; + } + + if (Q_stricmp(token.string, "shadowColor") == 0) { + if (!PC_Color_Parse(handle, &cgDC.Assets.shadowColor)) { + return qfalse; + } + cgDC.Assets.shadowFadeClamp = cgDC.Assets.shadowColor[3]; + continue; + } + } + return qfalse; +} + +void CG_ParseMenu(const char *menuFile) { + pc_token_t token; + int handle; + + handle = trap_PC_LoadSource(menuFile); + if (!handle) + handle = trap_PC_LoadSource("ui/testhud.menu"); + if (!handle) + return; + + while ( 1 ) { + if (!trap_PC_ReadToken( handle, &token )) { + break; + } + + //if ( Q_stricmp( token, "{" ) ) { + // Com_Printf( "Missing { in menu file\n" ); + // break; + //} + + //if ( menuCount == MAX_MENUS ) { + // Com_Printf( "Too many menus!\n" ); + // break; + //} + + if ( token.string[0] == '}' ) { + break; + } + + if (Q_stricmp(token.string, "assetGlobalDef") == 0) { + if (CG_Asset_Parse(handle)) { + continue; + } else { + break; + } + } + + + if (Q_stricmp(token.string, "menudef") == 0) { + // start a new menu + Menu_New(handle); + } + } + trap_PC_FreeSource(handle); +} + +qboolean CG_Load_Menu(char **p) { + char *token; + + token = COM_ParseExt(p, qtrue); + + if (token[0] != '{') { + return qfalse; + } + + while ( 1 ) { + + token = COM_ParseExt(p, qtrue); + + if (Q_stricmp(token, "}") == 0) { + return qtrue; + } + + if ( !token || token[0] == 0 ) { + return qfalse; + } + + CG_ParseMenu(token); + } + return qfalse; +} + + + +void CG_LoadMenus(const char *menuFile) { + char *token; + char *p; + int len, start; + fileHandle_t f; + static char buf[MAX_MENUDEFFILE]; + + start = trap_Milliseconds(); + + len = trap_FS_FOpenFile( menuFile, &f, FS_READ ); + if ( !f ) { + trap_Error( va( S_COLOR_YELLOW "menu file not found: %s, using default\n", menuFile ) ); + len = trap_FS_FOpenFile( "ui/hud.txt", &f, FS_READ ); + if (!f) { + trap_Error( va( S_COLOR_RED "default menu file not found: ui/hud.txt, unable to continue!\n") ); + } + } + + if ( len >= MAX_MENUDEFFILE ) { + trap_Error( va( S_COLOR_RED "menu file too large: %s is %i, max allowed is %i", menuFile, len, MAX_MENUDEFFILE ) ); + trap_FS_FCloseFile( f ); + return; + } + + trap_FS_Read( buf, len, f ); + buf[len] = 0; + trap_FS_FCloseFile( f ); + + COM_Compress(buf); + + Menu_Reset(); + + p = buf; + + while ( 1 ) { + token = COM_ParseExt( &p, qtrue ); + if( !token || token[0] == 0 || token[0] == '}') { + break; + } + + //if ( Q_stricmp( token, "{" ) ) { + // Com_Printf( "Missing { in menu file\n" ); + // break; + //} + + //if ( menuCount == MAX_MENUS ) { + // Com_Printf( "Too many menus!\n" ); + // break; + //} + + if ( Q_stricmp( token, "}" ) == 0 ) { + break; + } + + if (Q_stricmp(token, "loadmenu") == 0) { + if (CG_Load_Menu(&p)) { + continue; + } else { + break; + } + } + } + + Com_Printf("UI menu load time = %d milli seconds\n", trap_Milliseconds() - start); + +} + + + +static qboolean CG_OwnerDrawHandleKey(int ownerDraw, int flags, float *special, int key) { + return qfalse; +} + + +static int CG_FeederCount(float feederID) { + int i, count; + count = 0; + if (feederID == FEEDER_REDTEAM_LIST) { + for (i = 0; i < cg.numScores; i++) { + if (cg.scores[i].team == TEAM_RED) { + count++; + } + } + } else if (feederID == FEEDER_BLUETEAM_LIST) { + for (i = 0; i < cg.numScores; i++) { + if (cg.scores[i].team == TEAM_BLUE) { + count++; + } + } + } else if (feederID == FEEDER_SCOREBOARD) { + return cg.numScores; + } + return count; +} + + +void CG_SetScoreSelection(void *p) { + menuDef_t *menu = (menuDef_t*)p; + playerState_t *ps = &cg.snap->ps; + int i, red, blue; + red = blue = 0; + for (i = 0; i < cg.numScores; i++) { + if (cg.scores[i].team == TEAM_RED) { + red++; + } else if (cg.scores[i].team == TEAM_BLUE) { + blue++; + } + if (ps->clientNum == cg.scores[i].client) { + cg.selectedScore = i; + } + } + + if (menu == NULL) { + // just interested in setting the selected score + return; + } + + if ( cgs.gametype >= GT_TEAM ) { + int feeder = FEEDER_REDTEAM_LIST; + i = red; + if (cg.scores[cg.selectedScore].team == TEAM_BLUE) { + feeder = FEEDER_BLUETEAM_LIST; + i = blue; + } + Menu_SetFeederSelection(menu, feeder, i, NULL); + } else { + Menu_SetFeederSelection(menu, FEEDER_SCOREBOARD, cg.selectedScore, NULL); + } +} + +// FIXME: might need to cache this info +static clientInfo_t * CG_InfoFromScoreIndex(int index, int team, int *scoreIndex) { + int i, count; + if ( cgs.gametype >= GT_TEAM ) { + count = 0; + for (i = 0; i < cg.numScores; i++) { + if (cg.scores[i].team == team) { + if (count == index) { + *scoreIndex = i; + return &cgs.clientinfo[cg.scores[i].client]; + } + count++; + } + } + } + *scoreIndex = index; + return &cgs.clientinfo[ cg.scores[index].client ]; +} + +static const char *CG_FeederItemText(float feederID, int index, int column, qhandle_t *handle) { + gitem_t *item; + int scoreIndex = 0; + clientInfo_t *info = NULL; + int team = -1; + score_t *sp = NULL; + + *handle = -1; + + if (feederID == FEEDER_REDTEAM_LIST) { + team = TEAM_RED; + } else if (feederID == FEEDER_BLUETEAM_LIST) { + team = TEAM_BLUE; + } + + info = CG_InfoFromScoreIndex(index, team, &scoreIndex); + sp = &cg.scores[scoreIndex]; + + if (info && info->infoValid) { + switch (column) { + case 0: + if ( info->powerups & ( 1 << PW_NEUTRALFLAG ) ) { + item = BG_FindItemForPowerup( PW_NEUTRALFLAG ); + *handle = cg_items[ ITEM_INDEX(item) ].icon; + } else if ( info->powerups & ( 1 << PW_REDFLAG ) ) { + item = BG_FindItemForPowerup( PW_REDFLAG ); + *handle = cg_items[ ITEM_INDEX(item) ].icon; + } else if ( info->powerups & ( 1 << PW_BLUEFLAG ) ) { + item = BG_FindItemForPowerup( PW_BLUEFLAG ); + *handle = cg_items[ ITEM_INDEX(item) ].icon; + } else { + if ( info->botSkill > 0 && info->botSkill <= 5 ) { + *handle = cgs.media.botSkillShaders[ info->botSkill - 1 ]; + } else if ( info->handicap < 100 ) { + return va("%i", info->handicap ); + } + } + break; + case 1: + if (team == -1) { + return ""; + } else { + *handle = CG_StatusHandle(info->teamTask); + } + break; + case 2: + if ( cg.snap->ps.stats[ STAT_CLIENTS_READY ] & ( 1 << sp->client ) ) { + return "Ready"; + } + if (team == -1) { + if (cgs.gametype == GT_TOURNAMENT) { + return va("%i/%i", info->wins, info->losses); + } else if (info->infoValid && info->team == TEAM_SPECTATOR ) { + return "Spectator"; + } else { + return ""; + } + } else { + if (info->teamLeader) { + return "Leader"; + } + } + break; + case 3: + return info->name; + break; + case 4: + return va("%i", info->score); + break; + case 5: + return va("%4i", sp->time); + break; + case 6: + if ( sp->ping == -1 ) { + return "connecting"; + } + return va("%4i", sp->ping); + break; + } + } + + return ""; +} + +static qhandle_t CG_FeederItemImage(float feederID, int index) { + return 0; +} + +static void CG_FeederSelection(float feederID, int index) { + if ( cgs.gametype >= GT_TEAM ) { + int i, count; + int team = (feederID == FEEDER_REDTEAM_LIST) ? TEAM_RED : TEAM_BLUE; + count = 0; + for (i = 0; i < cg.numScores; i++) { + if (cg.scores[i].team == team) { + if (index == count) { + cg.selectedScore = i; + } + count++; + } + } + } else { + cg.selectedScore = index; + } +} +#endif + +#ifdef MISSIONPACK +static float CG_Cvar_Get(const char *cvar) { + char buff[128]; + memset(buff, 0, sizeof(buff)); + trap_Cvar_VariableStringBuffer(cvar, buff, sizeof(buff)); + return atof(buff); +} +#endif + +#ifdef MISSIONPACK +void CG_Text_PaintWithCursor(float x, float y, float scale, vec4_t color, const char *text, int cursorPos, char cursor, int limit, int style) { + CG_Text_Paint(x, y, scale, color, text, 0, limit, style); +} + +static int CG_OwnerDrawWidth(int ownerDraw, float scale) { + switch (ownerDraw) { + case CG_GAME_TYPE: + return CG_Text_Width(CG_GameTypeString(), scale, 0); + case CG_GAME_STATUS: + return CG_Text_Width(CG_GetGameStatusText(), scale, 0); + break; + case CG_KILLER: + return CG_Text_Width(CG_GetKillerText(), scale, 0); + break; + case CG_RED_NAME: + return CG_Text_Width(cg_redTeamName.string, scale, 0); + break; + case CG_BLUE_NAME: + return CG_Text_Width(cg_blueTeamName.string, scale, 0); + break; + + + } + return 0; +} + +static int CG_PlayCinematic(const char *name, float x, float y, float w, float h) { + return trap_CIN_PlayCinematic(name, x, y, w, h, CIN_loop); +} + +static void CG_StopCinematic(int handle) { + trap_CIN_StopCinematic(handle); +} + +static void CG_DrawCinematic(int handle, float x, float y, float w, float h) { + trap_CIN_SetExtents(handle, x, y, w, h); + trap_CIN_DrawCinematic(handle); +} + +static void CG_RunCinematicFrame(int handle) { + trap_CIN_RunCinematic(handle); +} + +/* +================= +CG_LoadHudMenu(); + +================= +*/ +void CG_LoadHudMenu( void ) { + char buff[1024]; + const char *hudSet; + + cgDC.registerShaderNoMip = &trap_R_RegisterShaderNoMip; + cgDC.setColor = &trap_R_SetColor; + cgDC.drawHandlePic = &CG_DrawPic; + cgDC.drawStretchPic = &trap_R_DrawStretchPic; + cgDC.drawText = &CG_Text_Paint; + cgDC.textWidth = &CG_Text_Width; + cgDC.textHeight = &CG_Text_Height; + cgDC.registerModel = &trap_R_RegisterModel; + cgDC.modelBounds = &trap_R_ModelBounds; + cgDC.fillRect = &CG_FillRect; + cgDC.drawRect = &CG_DrawRect; + cgDC.drawSides = &CG_DrawSides; + cgDC.drawTopBottom = &CG_DrawTopBottom; + cgDC.clearScene = &trap_R_ClearScene; + cgDC.addRefEntityToScene = &trap_R_AddRefEntityToScene; + cgDC.renderScene = &trap_R_RenderScene; + cgDC.registerFont = &trap_R_RegisterFont; + cgDC.ownerDrawItem = &CG_OwnerDraw; + cgDC.getValue = &CG_GetValue; + cgDC.ownerDrawVisible = &CG_OwnerDrawVisible; + cgDC.runScript = &CG_RunMenuScript; + cgDC.getTeamColor = &CG_GetTeamColor; + cgDC.setCVar = trap_Cvar_Set; + cgDC.getCVarString = trap_Cvar_VariableStringBuffer; + cgDC.getCVarValue = CG_Cvar_Get; + cgDC.drawTextWithCursor = &CG_Text_PaintWithCursor; + //cgDC.setOverstrikeMode = &trap_Key_SetOverstrikeMode; + //cgDC.getOverstrikeMode = &trap_Key_GetOverstrikeMode; + cgDC.startLocalSound = &trap_S_StartLocalSound; + cgDC.ownerDrawHandleKey = &CG_OwnerDrawHandleKey; + cgDC.feederCount = &CG_FeederCount; + cgDC.feederItemImage = &CG_FeederItemImage; + cgDC.feederItemText = &CG_FeederItemText; + cgDC.feederSelection = &CG_FeederSelection; + //cgDC.setBinding = &trap_Key_SetBinding; + //cgDC.getBindingBuf = &trap_Key_GetBindingBuf; + //cgDC.keynumToStringBuf = &trap_Key_KeynumToStringBuf; + //cgDC.executeText = &trap_Cmd_ExecuteText; + cgDC.Error = &Com_Error; + cgDC.Print = &Com_Printf; + cgDC.ownerDrawWidth = &CG_OwnerDrawWidth; + //cgDC.Pause = &CG_Pause; + cgDC.registerSound = &trap_S_RegisterSound; + cgDC.startBackgroundTrack = &trap_S_StartBackgroundTrack; + cgDC.stopBackgroundTrack = &trap_S_StopBackgroundTrack; + cgDC.playCinematic = &CG_PlayCinematic; + cgDC.stopCinematic = &CG_StopCinematic; + cgDC.drawCinematic = &CG_DrawCinematic; + cgDC.runCinematicFrame = &CG_RunCinematicFrame; + + Init_Display(&cgDC); + + Menu_Reset(); + + trap_Cvar_VariableStringBuffer("cg_hudFiles", buff, sizeof(buff)); + hudSet = buff; + if (hudSet[0] == '\0') { + hudSet = "ui/hud.txt"; + } + + CG_LoadMenus(hudSet); +} + +void CG_AssetCache( void ) { + //if (Assets.textFont == NULL) { + // trap_R_RegisterFont("fonts/arial.ttf", 72, &Assets.textFont); + //} + //Assets.background = trap_R_RegisterShaderNoMip( ASSET_BACKGROUND ); + //Com_Printf("Menu Size: %i bytes\n", sizeof(Menus)); + cgDC.Assets.gradientBar = trap_R_RegisterShaderNoMip( ASSET_GRADIENTBAR ); + cgDC.Assets.fxBasePic = trap_R_RegisterShaderNoMip( ART_FX_BASE ); + cgDC.Assets.fxPic[0] = trap_R_RegisterShaderNoMip( ART_FX_RED ); + cgDC.Assets.fxPic[1] = trap_R_RegisterShaderNoMip( ART_FX_YELLOW ); + cgDC.Assets.fxPic[2] = trap_R_RegisterShaderNoMip( ART_FX_GREEN ); + cgDC.Assets.fxPic[3] = trap_R_RegisterShaderNoMip( ART_FX_TEAL ); + cgDC.Assets.fxPic[4] = trap_R_RegisterShaderNoMip( ART_FX_BLUE ); + cgDC.Assets.fxPic[5] = trap_R_RegisterShaderNoMip( ART_FX_CYAN ); + cgDC.Assets.fxPic[6] = trap_R_RegisterShaderNoMip( ART_FX_WHITE ); + cgDC.Assets.scrollBar = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR ); + cgDC.Assets.scrollBarArrowDown = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWDOWN ); + cgDC.Assets.scrollBarArrowUp = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWUP ); + cgDC.Assets.scrollBarArrowLeft = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWLEFT ); + cgDC.Assets.scrollBarArrowRight = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWRIGHT ); + cgDC.Assets.scrollBarThumb = trap_R_RegisterShaderNoMip( ASSET_SCROLL_THUMB ); + cgDC.Assets.sliderBar = trap_R_RegisterShaderNoMip( ASSET_SLIDER_BAR ); + cgDC.Assets.sliderThumb = trap_R_RegisterShaderNoMip( ASSET_SLIDER_THUMB ); +} +#endif +/* +================= +CG_Init + +Called after every level change or subsystem restart +Will perform callbacks to make the loading info screen update. +================= +*/ +void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ) { + const char *s; + + // clear everything + memset( &cgs, 0, sizeof( cgs ) ); + memset( &cg, 0, sizeof( cg ) ); + memset( cg_entities, 0, sizeof(cg_entities) ); + memset( cg_weapons, 0, sizeof(cg_weapons) ); + memset( cg_items, 0, sizeof(cg_items) ); + + cg.clientNum = clientNum; + + cgs.processedSnapshotNum = serverMessageNum; + cgs.serverCommandSequence = serverCommandSequence; + + // load a few needed things before we do any screen updates + cgs.media.charsetShader = trap_R_RegisterShader( "gfx/2d/bigchars" ); + cgs.media.whiteShader = trap_R_RegisterShader( "white" ); + cgs.media.charsetProp = trap_R_RegisterShaderNoMip( "menu/art/font1_prop.tga" ); + cgs.media.charsetPropGlow = trap_R_RegisterShaderNoMip( "menu/art/font1_prop_glo.tga" ); + cgs.media.charsetPropB = trap_R_RegisterShaderNoMip( "menu/art/font2_prop.tga" ); + + CG_RegisterCvars(); + + CG_InitConsoleCommands(); + + cg.weaponSelect = WP_MACHINEGUN; + + cgs.redflag = cgs.blueflag = -1; // For compatibily, default to unset for + cgs.flagStatus = -1; + // old servers + + // get the rendering configuration from the client system + trap_GetGlconfig( &cgs.glconfig ); + cgs.screenXScale = cgs.glconfig.vidWidth / 640.0; + cgs.screenYScale = cgs.glconfig.vidHeight / 480.0; + + // get the gamestate from the client system + trap_GetGameState( &cgs.gameState ); + + // check version + s = CG_ConfigString( CS_GAME_VERSION ); + if ( strcmp( s, GAME_VERSION ) ) { + CG_Error( "Client/Server game mismatch: %s/%s", GAME_VERSION, s ); + } + + s = CG_ConfigString( CS_LEVEL_START_TIME ); + cgs.levelStartTime = atoi( s ); + + CG_ParseServerinfo(); + + // load the new map + CG_LoadingString( "collision map" ); + + trap_CM_LoadMap( cgs.mapname ); + +#ifdef MISSIONPACK + String_Init(); +#endif + + cg.loading = qtrue; // force players to load instead of defer + + CG_LoadingString( "sounds" ); + + CG_RegisterSounds(); + + CG_LoadingString( "graphics" ); + + CG_RegisterGraphics(); + + CG_LoadingString( "clients" ); + + CG_RegisterClients(); // if low on memory, some clients will be deferred + +#ifdef MISSIONPACK + CG_AssetCache(); + CG_LoadHudMenu(); // load new hud stuff +#endif + + cg.loading = qfalse; // future players will be deferred + + CG_InitLocalEntities(); + + CG_InitMarkPolys(); + + // remove the last loading update + cg.infoScreenText[0] = 0; + + // Make sure we have update values (scores) + CG_SetConfigValues(); + + CG_StartMusic(); + + CG_LoadingString( "" ); + +#ifdef MISSIONPACK + CG_InitTeamChat(); +#endif + + CG_ShaderStateChanged(); + + trap_S_ClearLoopingSounds( qtrue ); +} + +/* +================= +CG_Shutdown + +Called before every level change or subsystem restart +================= +*/ +void CG_Shutdown( void ) { + // some mods may need to do cleanup work here, + // like closing files or archiving session data +} + + +/* +================== +CG_EventHandling +================== + type 0 - no event handling + 1 - team menu + 2 - hud editor + +*/ +#ifndef MISSIONPACK +void CG_EventHandling(int type) { +} + + + +void CG_KeyEvent(int key, qboolean down) { +} + +void CG_MouseEvent(int x, int y) { +} +#endif + diff --git a/reaction/engine/code/cgame/cg_marks.c b/reaction/engine/code/cgame/cg_marks.c new file mode 100644 index 00000000..71f74b16 --- /dev/null +++ b/reaction/engine/code/cgame/cg_marks.c @@ -0,0 +1,2274 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_marks.c -- wall marks + +#include "cg_local.h" + +/* +=================================================================== + +MARK POLYS + +=================================================================== +*/ + + +markPoly_t cg_activeMarkPolys; // double linked list +markPoly_t *cg_freeMarkPolys; // single linked list +markPoly_t cg_markPolys[MAX_MARK_POLYS]; +static int markTotal; + +/* +=================== +CG_InitMarkPolys + +This is called at startup and for tournement restarts +=================== +*/ +void CG_InitMarkPolys( void ) { + int i; + + memset( cg_markPolys, 0, sizeof(cg_markPolys) ); + + cg_activeMarkPolys.nextMark = &cg_activeMarkPolys; + cg_activeMarkPolys.prevMark = &cg_activeMarkPolys; + cg_freeMarkPolys = cg_markPolys; + for ( i = 0 ; i < MAX_MARK_POLYS - 1 ; i++ ) { + cg_markPolys[i].nextMark = &cg_markPolys[i+1]; + } +} + + +/* +================== +CG_FreeMarkPoly +================== +*/ +void CG_FreeMarkPoly( markPoly_t *le ) { + if ( !le->prevMark ) { + CG_Error( "CG_FreeLocalEntity: not active" ); + } + + // remove from the doubly linked active list + le->prevMark->nextMark = le->nextMark; + le->nextMark->prevMark = le->prevMark; + + // the free list is only singly linked + le->nextMark = cg_freeMarkPolys; + cg_freeMarkPolys = le; +} + +/* +=================== +CG_AllocMark + +Will allways succeed, even if it requires freeing an old active mark +=================== +*/ +markPoly_t *CG_AllocMark( void ) { + markPoly_t *le; + int time; + + if ( !cg_freeMarkPolys ) { + // no free entities, so free the one at the end of the chain + // remove the oldest active entity + time = cg_activeMarkPolys.prevMark->time; + while (cg_activeMarkPolys.prevMark && time == cg_activeMarkPolys.prevMark->time) { + CG_FreeMarkPoly( cg_activeMarkPolys.prevMark ); + } + } + + le = cg_freeMarkPolys; + cg_freeMarkPolys = cg_freeMarkPolys->nextMark; + + memset( le, 0, sizeof( *le ) ); + + // link into the active list + le->nextMark = cg_activeMarkPolys.nextMark; + le->prevMark = &cg_activeMarkPolys; + cg_activeMarkPolys.nextMark->prevMark = le; + cg_activeMarkPolys.nextMark = le; + return le; +} + + + +/* +================= +CG_ImpactMark + +origin should be a point within a unit of the plane +dir should be the plane normal + +temporary marks will not be stored or randomly oriented, but immediately +passed to the renderer. +================= +*/ +#define MAX_MARK_FRAGMENTS 128 +#define MAX_MARK_POINTS 384 + +void CG_ImpactMark( qhandle_t markShader, const vec3_t origin, const vec3_t dir, + float orientation, float red, float green, float blue, float alpha, + qboolean alphaFade, float radius, qboolean temporary ) { + vec3_t axis[3]; + float texCoordScale; + vec3_t originalPoints[4]; + byte colors[4]; + int i, j; + int numFragments; + markFragment_t markFragments[MAX_MARK_FRAGMENTS], *mf; + vec3_t markPoints[MAX_MARK_POINTS]; + vec3_t projection; + + if ( !cg_addMarks.integer ) { + return; + } + + if ( radius <= 0 ) { + CG_Error( "CG_ImpactMark called with <= 0 radius" ); + } + + //if ( markTotal >= MAX_MARK_POLYS ) { + // return; + //} + + // create the texture axis + VectorNormalize2( dir, axis[0] ); + PerpendicularVector( axis[1], axis[0] ); + RotatePointAroundVector( axis[2], axis[0], axis[1], orientation ); + CrossProduct( axis[0], axis[2], axis[1] ); + + texCoordScale = 0.5 * 1.0 / radius; + + // create the full polygon + for ( i = 0 ; i < 3 ; i++ ) { + originalPoints[0][i] = origin[i] - radius * axis[1][i] - radius * axis[2][i]; + originalPoints[1][i] = origin[i] + radius * axis[1][i] - radius * axis[2][i]; + originalPoints[2][i] = origin[i] + radius * axis[1][i] + radius * axis[2][i]; + originalPoints[3][i] = origin[i] - radius * axis[1][i] + radius * axis[2][i]; + } + + // get the fragments + VectorScale( dir, -20, projection ); + numFragments = trap_CM_MarkFragments( 4, (void *)originalPoints, + projection, MAX_MARK_POINTS, markPoints[0], + MAX_MARK_FRAGMENTS, markFragments ); + + colors[0] = red * 255; + colors[1] = green * 255; + colors[2] = blue * 255; + colors[3] = alpha * 255; + + for ( i = 0, mf = markFragments ; i < numFragments ; i++, mf++ ) { + polyVert_t *v; + polyVert_t verts[MAX_VERTS_ON_POLY]; + markPoly_t *mark; + + // we have an upper limit on the complexity of polygons + // that we store persistantly + if ( mf->numPoints > MAX_VERTS_ON_POLY ) { + mf->numPoints = MAX_VERTS_ON_POLY; + } + for ( j = 0, v = verts ; j < mf->numPoints ; j++, v++ ) { + vec3_t delta; + + VectorCopy( markPoints[mf->firstPoint + j], v->xyz ); + + VectorSubtract( v->xyz, origin, delta ); + v->st[0] = 0.5 + DotProduct( delta, axis[1] ) * texCoordScale; + v->st[1] = 0.5 + DotProduct( delta, axis[2] ) * texCoordScale; + *(int *)v->modulate = *(int *)colors; + } + + // if it is a temporary (shadow) mark, add it immediately and forget about it + if ( temporary ) { + trap_R_AddPolyToScene( markShader, mf->numPoints, verts ); + continue; + } + + // otherwise save it persistantly + mark = CG_AllocMark(); + mark->time = cg.time; + mark->alphaFade = alphaFade; + mark->markShader = markShader; + mark->poly.numVerts = mf->numPoints; + mark->color[0] = red; + mark->color[1] = green; + mark->color[2] = blue; + mark->color[3] = alpha; + memcpy( mark->verts, verts, mf->numPoints * sizeof( verts[0] ) ); + markTotal++; + } +} + + +/* +=============== +CG_AddMarks +=============== +*/ +#define MARK_TOTAL_TIME 10000 +#define MARK_FADE_TIME 1000 + +void CG_AddMarks( void ) { + int j; + markPoly_t *mp, *next; + int t; + int fade; + + if ( !cg_addMarks.integer ) { + return; + } + + mp = cg_activeMarkPolys.nextMark; + for ( ; mp != &cg_activeMarkPolys ; mp = next ) { + // grab next now, so if the local entity is freed we + // still have it + next = mp->nextMark; + + // see if it is time to completely remove it + if ( cg.time > mp->time + MARK_TOTAL_TIME ) { + CG_FreeMarkPoly( mp ); + continue; + } + + // fade out the energy bursts + if ( mp->markShader == cgs.media.energyMarkShader ) { + + fade = 450 - 450 * ( (cg.time - mp->time ) / 3000.0 ); + if ( fade < 255 ) { + if ( fade < 0 ) { + fade = 0; + } + if ( mp->verts[0].modulate[0] != 0 ) { + for ( j = 0 ; j < mp->poly.numVerts ; j++ ) { + mp->verts[j].modulate[0] = mp->color[0] * fade; + mp->verts[j].modulate[1] = mp->color[1] * fade; + mp->verts[j].modulate[2] = mp->color[2] * fade; + } + } + } + } + + // fade all marks out with time + t = mp->time + MARK_TOTAL_TIME - cg.time; + if ( t < MARK_FADE_TIME ) { + fade = 255 * t / MARK_FADE_TIME; + if ( mp->alphaFade ) { + for ( j = 0 ; j < mp->poly.numVerts ; j++ ) { + mp->verts[j].modulate[3] = fade; + } + } else { + for ( j = 0 ; j < mp->poly.numVerts ; j++ ) { + mp->verts[j].modulate[0] = mp->color[0] * fade; + mp->verts[j].modulate[1] = mp->color[1] * fade; + mp->verts[j].modulate[2] = mp->color[2] * fade; + } + } + } + + + trap_R_AddPolyToScene( mp->markShader, mp->poly.numVerts, mp->verts ); + } +} + +// cg_particles.c + +#define BLOODRED 2 +#define EMISIVEFADE 3 +#define GREY75 4 + +typedef struct particle_s +{ + struct particle_s *next; + + float time; + float endtime; + + vec3_t org; + vec3_t vel; + vec3_t accel; + int color; + float colorvel; + float alpha; + float alphavel; + int type; + qhandle_t pshader; + + float height; + float width; + + float endheight; + float endwidth; + + float start; + float end; + + float startfade; + qboolean rotate; + int snum; + + qboolean link; + + // Ridah + int shaderAnim; + int roll; + + int accumroll; + +} cparticle_t; + +typedef enum +{ + P_NONE, + P_WEATHER, + P_FLAT, + P_SMOKE, + P_ROTATE, + P_WEATHER_TURBULENT, + P_ANIM, // Ridah + P_BAT, + P_BLEED, + P_FLAT_SCALEUP, + P_FLAT_SCALEUP_FADE, + P_WEATHER_FLURRY, + P_SMOKE_IMPACT, + P_BUBBLE, + P_BUBBLE_TURBULENT, + P_SPRITE +} particle_type_t; + +#define MAX_SHADER_ANIMS 32 +#define MAX_SHADER_ANIM_FRAMES 64 + +static char *shaderAnimNames[MAX_SHADER_ANIMS] = { + "explode1", + NULL +}; +static qhandle_t shaderAnims[MAX_SHADER_ANIMS][MAX_SHADER_ANIM_FRAMES]; +static int shaderAnimCounts[MAX_SHADER_ANIMS] = { + 23 +}; +static float shaderAnimSTRatio[MAX_SHADER_ANIMS] = { + 1.0f +}; +static int numShaderAnims; +// done. + +#define PARTICLE_GRAVITY 40 +#define MAX_PARTICLES 1024 + +cparticle_t *active_particles, *free_particles; +cparticle_t particles[MAX_PARTICLES]; +int cl_numparticles = MAX_PARTICLES; + +qboolean initparticles = qfalse; +vec3_t pvforward, pvright, pvup; +vec3_t rforward, rright, rup; + +float oldtime; + +/* +=============== +CL_ClearParticles +=============== +*/ +void CG_ClearParticles (void) +{ + int i; + + memset( particles, 0, sizeof(particles) ); + + free_particles = &particles[0]; + active_particles = NULL; + + for (i=0 ;itype == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY + || p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) + {// create a front facing polygon + + if (p->type != P_WEATHER_FLURRY) + { + if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) + { + if (org[2] > p->end) + { + p->time = cg.time; + VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground + + p->org[2] = ( p->start + crandom () * 4 ); + + + if (p->type == P_BUBBLE_TURBULENT) + { + p->vel[0] = crandom() * 4; + p->vel[1] = crandom() * 4; + } + + } + } + else + { + if (org[2] < p->end) + { + p->time = cg.time; + VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground + + while (p->org[2] < p->end) + { + p->org[2] += (p->start - p->end); + } + + + if (p->type == P_WEATHER_TURBULENT) + { + p->vel[0] = crandom() * 16; + p->vel[1] = crandom() * 16; + } + + } + } + + + // Rafael snow pvs check + if (!p->link) + return; + + p->alpha = 1; + } + + // Ridah, had to do this or MAX_POLYS is being exceeded in village1.bsp + if (Distance( cg.snap->ps.origin, org ) > 1024) { + return; + } + // done. + + if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) + { + VectorMA (org, -p->height, pvup, point); + VectorMA (point, -p->width, pvright, point); + VectorCopy (point, verts[0].xyz); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255 * p->alpha; + + VectorMA (org, -p->height, pvup, point); + VectorMA (point, p->width, pvright, point); + VectorCopy (point, verts[1].xyz); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255 * p->alpha; + + VectorMA (org, p->height, pvup, point); + VectorMA (point, p->width, pvright, point); + VectorCopy (point, verts[2].xyz); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255 * p->alpha; + + VectorMA (org, p->height, pvup, point); + VectorMA (point, -p->width, pvright, point); + VectorCopy (point, verts[3].xyz); + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255 * p->alpha; + } + else + { + VectorMA (org, -p->height, pvup, point); + VectorMA (point, -p->width, pvright, point); + VectorCopy( point, TRIverts[0].xyz ); + TRIverts[0].st[0] = 1; + TRIverts[0].st[1] = 0; + TRIverts[0].modulate[0] = 255; + TRIverts[0].modulate[1] = 255; + TRIverts[0].modulate[2] = 255; + TRIverts[0].modulate[3] = 255 * p->alpha; + + VectorMA (org, p->height, pvup, point); + VectorMA (point, -p->width, pvright, point); + VectorCopy (point, TRIverts[1].xyz); + TRIverts[1].st[0] = 0; + TRIverts[1].st[1] = 0; + TRIverts[1].modulate[0] = 255; + TRIverts[1].modulate[1] = 255; + TRIverts[1].modulate[2] = 255; + TRIverts[1].modulate[3] = 255 * p->alpha; + + VectorMA (org, p->height, pvup, point); + VectorMA (point, p->width, pvright, point); + VectorCopy (point, TRIverts[2].xyz); + TRIverts[2].st[0] = 0; + TRIverts[2].st[1] = 1; + TRIverts[2].modulate[0] = 255; + TRIverts[2].modulate[1] = 255; + TRIverts[2].modulate[2] = 255; + TRIverts[2].modulate[3] = 255 * p->alpha; + } + + } + else if (p->type == P_SPRITE) + { + vec3_t rr, ru; + vec3_t rotate_ang; + + VectorSet (color, 1.0, 1.0, 0.5); + time = cg.time - p->time; + time2 = p->endtime - p->time; + ratio = time / time2; + + width = p->width + ( ratio * ( p->endwidth - p->width) ); + height = p->height + ( ratio * ( p->endheight - p->height) ); + + if (p->roll) { + vectoangles( cg.refdef.viewaxis[0], rotate_ang ); + rotate_ang[ROLL] += p->roll; + AngleVectors ( rotate_ang, NULL, rr, ru); + } + + if (p->roll) { + VectorMA (org, -height, ru, point); + VectorMA (point, -width, rr, point); + } else { + VectorMA (org, -height, pvup, point); + VectorMA (point, -width, pvright, point); + } + VectorCopy (point, verts[0].xyz); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255; + + if (p->roll) { + VectorMA (point, 2*height, ru, point); + } else { + VectorMA (point, 2*height, pvup, point); + } + VectorCopy (point, verts[1].xyz); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255; + + if (p->roll) { + VectorMA (point, 2*width, rr, point); + } else { + VectorMA (point, 2*width, pvright, point); + } + VectorCopy (point, verts[2].xyz); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255; + + if (p->roll) { + VectorMA (point, -2*height, ru, point); + } else { + VectorMA (point, -2*height, pvup, point); + } + VectorCopy (point, verts[3].xyz); + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255; + } + else if (p->type == P_SMOKE || p->type == P_SMOKE_IMPACT) + {// create a front rotating facing polygon + + if ( p->type == P_SMOKE_IMPACT && Distance( cg.snap->ps.origin, org ) > 1024) { + return; + } + + if (p->color == BLOODRED) + VectorSet (color, 0.22f, 0.0f, 0.0f); + else if (p->color == GREY75) + { + float len; + float greyit; + float val; + len = Distance (cg.snap->ps.origin, org); + if (!len) + len = 1; + + val = 4096/len; + greyit = 0.25 * val; + if (greyit > 0.5) + greyit = 0.5; + + VectorSet (color, greyit, greyit, greyit); + } + else + VectorSet (color, 1.0, 1.0, 1.0); + + time = cg.time - p->time; + time2 = p->endtime - p->time; + ratio = time / time2; + + if (cg.time > p->startfade) + { + invratio = 1 - ( (cg.time - p->startfade) / (p->endtime - p->startfade) ); + + if (p->color == EMISIVEFADE) + { + float fval; + fval = (invratio * invratio); + if (fval < 0) + fval = 0; + VectorSet (color, fval , fval , fval ); + } + invratio *= p->alpha; + } + else + invratio = 1 * p->alpha; + + if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) + invratio = 1; + + if (invratio > 1) + invratio = 1; + + width = p->width + ( ratio * ( p->endwidth - p->width) ); + height = p->height + ( ratio * ( p->endheight - p->height) ); + + if (p->type != P_SMOKE_IMPACT) + { + vec3_t temp; + + vectoangles (rforward, temp); + p->accumroll += p->roll; + temp[ROLL] += p->accumroll * 0.1; + AngleVectors ( temp, NULL, rright2, rup2); + } + else + { + VectorCopy (rright, rright2); + VectorCopy (rup, rup2); + } + + if (p->rotate) + { + VectorMA (org, -height, rup2, point); + VectorMA (point, -width, rright2, point); + } + else + { + VectorMA (org, -p->height, pvup, point); + VectorMA (point, -p->width, pvright, point); + } + VectorCopy (point, verts[0].xyz); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255 * color[0]; + verts[0].modulate[1] = 255 * color[1]; + verts[0].modulate[2] = 255 * color[2]; + verts[0].modulate[3] = 255 * invratio; + + if (p->rotate) + { + VectorMA (org, -height, rup2, point); + VectorMA (point, width, rright2, point); + } + else + { + VectorMA (org, -p->height, pvup, point); + VectorMA (point, p->width, pvright, point); + } + VectorCopy (point, verts[1].xyz); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255 * color[0]; + verts[1].modulate[1] = 255 * color[1]; + verts[1].modulate[2] = 255 * color[2]; + verts[1].modulate[3] = 255 * invratio; + + if (p->rotate) + { + VectorMA (org, height, rup2, point); + VectorMA (point, width, rright2, point); + } + else + { + VectorMA (org, p->height, pvup, point); + VectorMA (point, p->width, pvright, point); + } + VectorCopy (point, verts[2].xyz); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255 * color[0]; + verts[2].modulate[1] = 255 * color[1]; + verts[2].modulate[2] = 255 * color[2]; + verts[2].modulate[3] = 255 * invratio; + + if (p->rotate) + { + VectorMA (org, height, rup2, point); + VectorMA (point, -width, rright2, point); + } + else + { + VectorMA (org, p->height, pvup, point); + VectorMA (point, -p->width, pvright, point); + } + VectorCopy (point, verts[3].xyz); + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255 * color[0]; + verts[3].modulate[1] = 255 * color[1]; + verts[3].modulate[2] = 255 * color[2]; + verts[3].modulate[3] = 255 * invratio; + + } + else if (p->type == P_BLEED) + { + vec3_t rr, ru; + vec3_t rotate_ang; + float alpha; + + alpha = p->alpha; + + if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) + alpha = 1; + + if (p->roll) + { + vectoangles( cg.refdef.viewaxis[0], rotate_ang ); + rotate_ang[ROLL] += p->roll; + AngleVectors ( rotate_ang, NULL, rr, ru); + } + else + { + VectorCopy (pvup, ru); + VectorCopy (pvright, rr); + } + + VectorMA (org, -p->height, ru, point); + VectorMA (point, -p->width, rr, point); + VectorCopy (point, verts[0].xyz); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 111; + verts[0].modulate[1] = 19; + verts[0].modulate[2] = 9; + verts[0].modulate[3] = 255 * alpha; + + VectorMA (org, -p->height, ru, point); + VectorMA (point, p->width, rr, point); + VectorCopy (point, verts[1].xyz); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 111; + verts[1].modulate[1] = 19; + verts[1].modulate[2] = 9; + verts[1].modulate[3] = 255 * alpha; + + VectorMA (org, p->height, ru, point); + VectorMA (point, p->width, rr, point); + VectorCopy (point, verts[2].xyz); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 111; + verts[2].modulate[1] = 19; + verts[2].modulate[2] = 9; + verts[2].modulate[3] = 255 * alpha; + + VectorMA (org, p->height, ru, point); + VectorMA (point, -p->width, rr, point); + VectorCopy (point, verts[3].xyz); + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 111; + verts[3].modulate[1] = 19; + verts[3].modulate[2] = 9; + verts[3].modulate[3] = 255 * alpha; + + } + else if (p->type == P_FLAT_SCALEUP) + { + float width, height; + float sinR, cosR; + + if (p->color == BLOODRED) + VectorSet (color, 1, 1, 1); + else + VectorSet (color, 0.5, 0.5, 0.5); + + time = cg.time - p->time; + time2 = p->endtime - p->time; + ratio = time / time2; + + width = p->width + ( ratio * ( p->endwidth - p->width) ); + height = p->height + ( ratio * ( p->endheight - p->height) ); + + if (width > p->endwidth) + width = p->endwidth; + + if (height > p->endheight) + height = p->endheight; + + sinR = height * sin(DEG2RAD(p->roll)) * sqrt(2); + cosR = width * cos(DEG2RAD(p->roll)) * sqrt(2); + + VectorCopy (org, verts[0].xyz); + verts[0].xyz[0] -= sinR; + verts[0].xyz[1] -= cosR; + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255 * color[0]; + verts[0].modulate[1] = 255 * color[1]; + verts[0].modulate[2] = 255 * color[2]; + verts[0].modulate[3] = 255; + + VectorCopy (org, verts[1].xyz); + verts[1].xyz[0] -= cosR; + verts[1].xyz[1] += sinR; + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255 * color[0]; + verts[1].modulate[1] = 255 * color[1]; + verts[1].modulate[2] = 255 * color[2]; + verts[1].modulate[3] = 255; + + VectorCopy (org, verts[2].xyz); + verts[2].xyz[0] += sinR; + verts[2].xyz[1] += cosR; + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255 * color[0]; + verts[2].modulate[1] = 255 * color[1]; + verts[2].modulate[2] = 255 * color[2]; + verts[2].modulate[3] = 255; + + VectorCopy (org, verts[3].xyz); + verts[3].xyz[0] += cosR; + verts[3].xyz[1] -= sinR; + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255 * color[0]; + verts[3].modulate[1] = 255 * color[1]; + verts[3].modulate[2] = 255 * color[2]; + verts[3].modulate[3] = 255; + } + else if (p->type == P_FLAT) + { + + VectorCopy (org, verts[0].xyz); + verts[0].xyz[0] -= p->height; + verts[0].xyz[1] -= p->width; + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255; + + VectorCopy (org, verts[1].xyz); + verts[1].xyz[0] -= p->height; + verts[1].xyz[1] += p->width; + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255; + + VectorCopy (org, verts[2].xyz); + verts[2].xyz[0] += p->height; + verts[2].xyz[1] += p->width; + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255; + + VectorCopy (org, verts[3].xyz); + verts[3].xyz[0] += p->height; + verts[3].xyz[1] -= p->width; + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255; + + } + // Ridah + else if (p->type == P_ANIM) { + vec3_t rr, ru; + vec3_t rotate_ang; + int i, j; + + time = cg.time - p->time; + time2 = p->endtime - p->time; + ratio = time / time2; + if (ratio >= 1.0f) { + ratio = 0.9999f; + } + + width = p->width + ( ratio * ( p->endwidth - p->width) ); + height = p->height + ( ratio * ( p->endheight - p->height) ); + + // if we are "inside" this sprite, don't draw + if (Distance( cg.snap->ps.origin, org ) < width/1.5) { + return; + } + + i = p->shaderAnim; + j = (int)floor(ratio * shaderAnimCounts[p->shaderAnim]); + p->pshader = shaderAnims[i][j]; + + if (p->roll) { + vectoangles( cg.refdef.viewaxis[0], rotate_ang ); + rotate_ang[ROLL] += p->roll; + AngleVectors ( rotate_ang, NULL, rr, ru); + } + + if (p->roll) { + VectorMA (org, -height, ru, point); + VectorMA (point, -width, rr, point); + } else { + VectorMA (org, -height, pvup, point); + VectorMA (point, -width, pvright, point); + } + VectorCopy (point, verts[0].xyz); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255; + + if (p->roll) { + VectorMA (point, 2*height, ru, point); + } else { + VectorMA (point, 2*height, pvup, point); + } + VectorCopy (point, verts[1].xyz); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255; + + if (p->roll) { + VectorMA (point, 2*width, rr, point); + } else { + VectorMA (point, 2*width, pvright, point); + } + VectorCopy (point, verts[2].xyz); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255; + + if (p->roll) { + VectorMA (point, -2*height, ru, point); + } else { + VectorMA (point, -2*height, pvup, point); + } + VectorCopy (point, verts[3].xyz); + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255; + } + // done. + + if (!p->pshader) { +// (SA) temp commented out for DM +// CG_Printf ("CG_AddParticleToScene type %d p->pshader == ZERO\n", p->type); + return; + } + + if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY) + trap_R_AddPolyToScene( p->pshader, 3, TRIverts ); + else + trap_R_AddPolyToScene( p->pshader, 4, verts ); + +} + +// Ridah, made this static so it doesn't interfere with other files +static float roll = 0.0; + +/* +=============== +CG_AddParticles +=============== +*/ +void CG_AddParticles (void) +{ + cparticle_t *p, *next; + float alpha; + float time, time2; + vec3_t org; + int color; + cparticle_t *active, *tail; + int type; + vec3_t rotate_ang; + + if (!initparticles) + CG_ClearParticles (); + + VectorCopy( cg.refdef.viewaxis[0], pvforward ); + VectorCopy( cg.refdef.viewaxis[1], pvright ); + VectorCopy( cg.refdef.viewaxis[2], pvup ); + + vectoangles( cg.refdef.viewaxis[0], rotate_ang ); + roll += ((cg.time - oldtime) * 0.1) ; + rotate_ang[ROLL] += (roll*0.9); + AngleVectors ( rotate_ang, rforward, rright, rup); + + oldtime = cg.time; + + active = NULL; + tail = NULL; + + for (p=active_particles ; p ; p=next) + { + + next = p->next; + + time = (cg.time - p->time)*0.001; + + alpha = p->alpha + time*p->alphavel; + if (alpha <= 0) + { // faded out + p->next = free_particles; + free_particles = p; + p->type = 0; + p->color = 0; + p->alpha = 0; + continue; + } + + if (p->type == P_SMOKE || p->type == P_ANIM || p->type == P_BLEED || p->type == P_SMOKE_IMPACT) + { + if (cg.time > p->endtime) + { + p->next = free_particles; + free_particles = p; + p->type = 0; + p->color = 0; + p->alpha = 0; + + continue; + } + + } + + if (p->type == P_WEATHER_FLURRY) + { + if (cg.time > p->endtime) + { + p->next = free_particles; + free_particles = p; + p->type = 0; + p->color = 0; + p->alpha = 0; + + continue; + } + } + + + if (p->type == P_FLAT_SCALEUP_FADE) + { + if (cg.time > p->endtime) + { + p->next = free_particles; + free_particles = p; + p->type = 0; + p->color = 0; + p->alpha = 0; + continue; + } + + } + + if ((p->type == P_BAT || p->type == P_SPRITE) && p->endtime < 0) { + // temporary sprite + CG_AddParticleToScene (p, p->org, alpha); + p->next = free_particles; + free_particles = p; + p->type = 0; + p->color = 0; + p->alpha = 0; + continue; + } + + p->next = NULL; + if (!tail) + active = tail = p; + else + { + tail->next = p; + tail = p; + } + + if (alpha > 1.0) + alpha = 1; + + color = p->color; + + time2 = time*time; + + org[0] = p->org[0] + p->vel[0]*time + p->accel[0]*time2; + org[1] = p->org[1] + p->vel[1]*time + p->accel[1]*time2; + org[2] = p->org[2] + p->vel[2]*time + p->accel[2]*time2; + + type = p->type; + + CG_AddParticleToScene (p, org, alpha); + } + + active_particles = active; +} + +/* +====================== +CG_AddParticles +====================== +*/ +void CG_ParticleSnowFlurry (qhandle_t pshader, centity_t *cent) +{ + cparticle_t *p; + qboolean turb = qtrue; + + if (!pshader) + CG_Printf ("CG_ParticleSnowFlurry pshader == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->color = 0; + p->alpha = 0.90f; + p->alphavel = 0; + + p->start = cent->currentState.origin2[0]; + p->end = cent->currentState.origin2[1]; + + p->endtime = cg.time + cent->currentState.time; + p->startfade = cg.time + cent->currentState.time2; + + p->pshader = pshader; + + if (rand()%100 > 90) + { + p->height = 32; + p->width = 32; + p->alpha = 0.10f; + } + else + { + p->height = 1; + p->width = 1; + } + + p->vel[2] = -20; + + p->type = P_WEATHER_FLURRY; + + if (turb) + p->vel[2] = -10; + + VectorCopy(cent->currentState.origin, p->org); + + p->org[0] = p->org[0]; + p->org[1] = p->org[1]; + p->org[2] = p->org[2]; + + p->vel[0] = p->vel[1] = 0; + + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + p->vel[0] += cent->currentState.angles[0] * 32 + (crandom() * 16); + p->vel[1] += cent->currentState.angles[1] * 32 + (crandom() * 16); + p->vel[2] += cent->currentState.angles[2]; + + if (turb) + { + p->accel[0] = crandom () * 16; + p->accel[1] = crandom () * 16; + } + +} + +void CG_ParticleSnow (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum) +{ + cparticle_t *p; + + if (!pshader) + CG_Printf ("CG_ParticleSnow pshader == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->color = 0; + p->alpha = 0.40f; + p->alphavel = 0; + p->start = origin[2]; + p->end = origin2[2]; + p->pshader = pshader; + p->height = 1; + p->width = 1; + + p->vel[2] = -50; + + if (turb) + { + p->type = P_WEATHER_TURBULENT; + p->vel[2] = -50 * 1.3; + } + else + { + p->type = P_WEATHER; + } + + VectorCopy(origin, p->org); + + p->org[0] = p->org[0] + ( crandom() * range); + p->org[1] = p->org[1] + ( crandom() * range); + p->org[2] = p->org[2] + ( crandom() * (p->start - p->end)); + + p->vel[0] = p->vel[1] = 0; + + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + if (turb) + { + p->vel[0] = crandom() * 16; + p->vel[1] = crandom() * 16; + } + + // Rafael snow pvs check + p->snum = snum; + p->link = qtrue; + +} + +void CG_ParticleBubble (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum) +{ + cparticle_t *p; + float randsize; + + if (!pshader) + CG_Printf ("CG_ParticleSnow pshader == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->color = 0; + p->alpha = 0.40f; + p->alphavel = 0; + p->start = origin[2]; + p->end = origin2[2]; + p->pshader = pshader; + + randsize = 1 + (crandom() * 0.5); + + p->height = randsize; + p->width = randsize; + + p->vel[2] = 50 + ( crandom() * 10 ); + + if (turb) + { + p->type = P_BUBBLE_TURBULENT; + p->vel[2] = 50 * 1.3; + } + else + { + p->type = P_BUBBLE; + } + + VectorCopy(origin, p->org); + + p->org[0] = p->org[0] + ( crandom() * range); + p->org[1] = p->org[1] + ( crandom() * range); + p->org[2] = p->org[2] + ( crandom() * (p->start - p->end)); + + p->vel[0] = p->vel[1] = 0; + + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + if (turb) + { + p->vel[0] = crandom() * 4; + p->vel[1] = crandom() * 4; + } + + // Rafael snow pvs check + p->snum = snum; + p->link = qtrue; + +} + +void CG_ParticleSmoke (qhandle_t pshader, centity_t *cent) +{ + + // using cent->density = enttime + // cent->frame = startfade + cparticle_t *p; + + if (!pshader) + CG_Printf ("CG_ParticleSmoke == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + + p->endtime = cg.time + cent->currentState.time; + p->startfade = cg.time + cent->currentState.time2; + + p->color = 0; + p->alpha = 1.0; + p->alphavel = 0; + p->start = cent->currentState.origin[2]; + p->end = cent->currentState.origin2[2]; + p->pshader = pshader; + p->rotate = qfalse; + p->height = 8; + p->width = 8; + p->endheight = 32; + p->endwidth = 32; + p->type = P_SMOKE; + + VectorCopy(cent->currentState.origin, p->org); + + p->vel[0] = p->vel[1] = 0; + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + p->vel[2] = 5; + + if (cent->currentState.frame == 1)// reverse gravity + p->vel[2] *= -1; + + p->roll = 8 + (crandom() * 4); +} + + +void CG_ParticleBulletDebris (vec3_t org, vec3_t vel, int duration) +{ + + cparticle_t *p; + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + + p->endtime = cg.time + duration; + p->startfade = cg.time + duration/2; + + p->color = EMISIVEFADE; + p->alpha = 1.0; + p->alphavel = 0; + + p->height = 0.5; + p->width = 0.5; + p->endheight = 0.5; + p->endwidth = 0.5; + + p->pshader = cgs.media.tracerShader; + + p->type = P_SMOKE; + + VectorCopy(org, p->org); + + p->vel[0] = vel[0]; + p->vel[1] = vel[1]; + p->vel[2] = vel[2]; + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + p->accel[2] = -60; + p->vel[2] += -20; + +} + +/* +====================== +CG_ParticleExplosion +====================== +*/ + +void CG_ParticleExplosion (char *animStr, vec3_t origin, vec3_t vel, int duration, int sizeStart, int sizeEnd) +{ + cparticle_t *p; + int anim; + + if (animStr < (char *)10) + CG_Error( "CG_ParticleExplosion: animStr is probably an index rather than a string" ); + + // find the animation string + for (anim=0; shaderAnimNames[anim]; anim++) { + if (!Q_stricmp( animStr, shaderAnimNames[anim] )) + break; + } + if (!shaderAnimNames[anim]) { + CG_Error("CG_ParticleExplosion: unknown animation string: %s\n", animStr); + return; + } + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->alpha = 0.5; + p->alphavel = 0; + + if (duration < 0) { + duration *= -1; + p->roll = 0; + } else { + p->roll = crandom()*179; + } + + p->shaderAnim = anim; + + p->width = sizeStart; + p->height = sizeStart*shaderAnimSTRatio[anim]; // for sprites that are stretch in either direction + + p->endheight = sizeEnd; + p->endwidth = sizeEnd*shaderAnimSTRatio[anim]; + + p->endtime = cg.time + duration; + + p->type = P_ANIM; + + VectorCopy( origin, p->org ); + VectorCopy( vel, p->vel ); + VectorClear( p->accel ); + +} + +// Rafael Shrapnel +void CG_AddParticleShrapnel (localEntity_t *le) +{ + return; +} +// done. + +int CG_NewParticleArea (int num) +{ + // const char *str; + char *str; + char *token; + int type; + vec3_t origin, origin2; + int i; + float range = 0; + int turb; + int numparticles; + int snum; + + str = (char *) CG_ConfigString (num); + if (!str[0]) + return (0); + + // returns type 128 64 or 32 + token = COM_Parse (&str); + type = atoi (token); + + if (type == 1) + range = 128; + else if (type == 2) + range = 64; + else if (type == 3) + range = 32; + else if (type == 0) + range = 256; + else if (type == 4) + range = 8; + else if (type == 5) + range = 16; + else if (type == 6) + range = 32; + else if (type == 7) + range = 64; + + + for (i=0; i<3; i++) + { + token = COM_Parse (&str); + origin[i] = atof (token); + } + + for (i=0; i<3; i++) + { + token = COM_Parse (&str); + origin2[i] = atof (token); + } + + token = COM_Parse (&str); + numparticles = atoi (token); + + token = COM_Parse (&str); + turb = atoi (token); + + token = COM_Parse (&str); + snum = atoi (token); + + for (i=0; i= 4) + CG_ParticleBubble (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum); + else + CG_ParticleSnow (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum); + } + + return (1); +} + +void CG_SnowLink (centity_t *cent, qboolean particleOn) +{ + cparticle_t *p, *next; + int id; + + id = cent->currentState.frame; + + for (p=active_particles ; p ; p=next) + { + next = p->next; + + if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT) + { + if (p->snum == id) + { + if (particleOn) + p->link = qtrue; + else + p->link = qfalse; + } + } + + } +} + +void CG_ParticleImpactSmokePuff (qhandle_t pshader, vec3_t origin) +{ + cparticle_t *p; + + if (!pshader) + CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->alpha = 0.25; + p->alphavel = 0; + p->roll = crandom()*179; + + p->pshader = pshader; + + p->endtime = cg.time + 1000; + p->startfade = cg.time + 100; + + p->width = rand()%4 + 8; + p->height = rand()%4 + 8; + + p->endheight = p->height *2; + p->endwidth = p->width * 2; + + p->endtime = cg.time + 500; + + p->type = P_SMOKE_IMPACT; + + VectorCopy( origin, p->org ); + VectorSet(p->vel, 0, 0, 20); + VectorSet(p->accel, 0, 0, 20); + + p->rotate = qtrue; +} + +void CG_Particle_Bleed (qhandle_t pshader, vec3_t start, vec3_t dir, int fleshEntityNum, int duration) +{ + cparticle_t *p; + + if (!pshader) + CG_Printf ("CG_Particle_Bleed pshader == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->alpha = 1.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = pshader; + + p->endtime = cg.time + duration; + + if (fleshEntityNum) + p->startfade = cg.time; + else + p->startfade = cg.time + 100; + + p->width = 4; + p->height = 4; + + p->endheight = 4+rand()%3; + p->endwidth = p->endheight; + + p->type = P_SMOKE; + + VectorCopy( start, p->org ); + p->vel[0] = 0; + p->vel[1] = 0; + p->vel[2] = -20; + VectorClear( p->accel ); + + p->rotate = qfalse; + + p->roll = rand()%179; + + p->color = BLOODRED; + p->alpha = 0.75; + +} + +void CG_Particle_OilParticle (qhandle_t pshader, centity_t *cent) +{ + cparticle_t *p; + + int time; + int time2; + float ratio; + + float duration = 1500; + + time = cg.time; + time2 = cg.time + cent->currentState.time; + + ratio =(float)1 - ((float)time / (float)time2); + + if (!pshader) + CG_Printf ("CG_Particle_OilParticle == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->alpha = 1.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = pshader; + + p->endtime = cg.time + duration; + + p->startfade = p->endtime; + + p->width = 1; + p->height = 3; + + p->endheight = 3; + p->endwidth = 1; + + p->type = P_SMOKE; + + VectorCopy(cent->currentState.origin, p->org ); + + p->vel[0] = (cent->currentState.origin2[0] * (16 * ratio)); + p->vel[1] = (cent->currentState.origin2[1] * (16 * ratio)); + p->vel[2] = (cent->currentState.origin2[2]); + + p->snum = 1.0f; + + VectorClear( p->accel ); + + p->accel[2] = -20; + + p->rotate = qfalse; + + p->roll = rand()%179; + + p->alpha = 0.75; + +} + + +void CG_Particle_OilSlick (qhandle_t pshader, centity_t *cent) +{ + cparticle_t *p; + + if (!pshader) + CG_Printf ("CG_Particle_OilSlick == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + + if (cent->currentState.angles2[2]) + p->endtime = cg.time + cent->currentState.angles2[2]; + else + p->endtime = cg.time + 60000; + + p->startfade = p->endtime; + + p->alpha = 1.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = pshader; + + if (cent->currentState.angles2[0] || cent->currentState.angles2[1]) + { + p->width = cent->currentState.angles2[0]; + p->height = cent->currentState.angles2[0]; + + p->endheight = cent->currentState.angles2[1]; + p->endwidth = cent->currentState.angles2[1]; + } + else + { + p->width = 8; + p->height = 8; + + p->endheight = 16; + p->endwidth = 16; + } + + p->type = P_FLAT_SCALEUP; + + p->snum = 1.0; + + VectorCopy(cent->currentState.origin, p->org ); + + p->org[2]+= 0.55 + (crandom() * 0.5); + + p->vel[0] = 0; + p->vel[1] = 0; + p->vel[2] = 0; + VectorClear( p->accel ); + + p->rotate = qfalse; + + p->roll = rand()%179; + + p->alpha = 0.75; + +} + +void CG_OilSlickRemove (centity_t *cent) +{ + cparticle_t *p, *next; + int id; + + id = 1.0f; + + if (!id) + CG_Printf ("CG_OilSlickRevove NULL id\n"); + + for (p=active_particles ; p ; p=next) + { + next = p->next; + + if (p->type == P_FLAT_SCALEUP) + { + if (p->snum == id) + { + p->endtime = cg.time + 100; + p->startfade = p->endtime; + p->type = P_FLAT_SCALEUP_FADE; + + } + } + + } +} + +qboolean ValidBloodPool (vec3_t start) +{ +#define EXTRUDE_DIST 0.5 + + vec3_t angles; + vec3_t right, up; + vec3_t this_pos, x_pos, center_pos, end_pos; + float x, y; + float fwidth, fheight; + trace_t trace; + vec3_t normal; + + fwidth = 16; + fheight = 16; + + VectorSet (normal, 0, 0, 1); + + vectoangles (normal, angles); + AngleVectors (angles, NULL, right, up); + + VectorMA (start, EXTRUDE_DIST, normal, center_pos); + + for (x= -fwidth/2; xendpos, start); + legit = ValidBloodPool (start); + + if (!legit) + return; + + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + + p->endtime = cg.time + 3000; + p->startfade = p->endtime; + + p->alpha = 1.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = pshader; + + rndSize = 0.4 + random()*0.6; + + p->width = 8*rndSize; + p->height = 8*rndSize; + + p->endheight = 16*rndSize; + p->endwidth = 16*rndSize; + + p->type = P_FLAT_SCALEUP; + + VectorCopy(start, p->org ); + + p->vel[0] = 0; + p->vel[1] = 0; + p->vel[2] = 0; + VectorClear( p->accel ); + + p->rotate = qfalse; + + p->roll = rand()%179; + + p->alpha = 0.75; + + p->color = BLOODRED; +} + +#define NORMALSIZE 16 +#define LARGESIZE 32 + +void CG_ParticleBloodCloud (centity_t *cent, vec3_t origin, vec3_t dir) +{ + float length; + float dist; + float crittersize; + vec3_t angles, forward; + vec3_t point; + cparticle_t *p; + int i; + + dist = 0; + + length = VectorLength (dir); + vectoangles (dir, angles); + AngleVectors (angles, forward, NULL, NULL); + + crittersize = LARGESIZE; + + if (length) + dist = length / crittersize; + + if (dist < 1) + dist = 1; + + VectorCopy (origin, point); + + for (i=0; inext; + p->next = active_particles; + active_particles = p; + + p->time = cg.time; + p->alpha = 1.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = cgs.media.smokePuffShader; + + p->endtime = cg.time + 350 + (crandom() * 100); + + p->startfade = cg.time; + + p->width = LARGESIZE; + p->height = LARGESIZE; + p->endheight = LARGESIZE; + p->endwidth = LARGESIZE; + + p->type = P_SMOKE; + + VectorCopy( origin, p->org ); + + p->vel[0] = 0; + p->vel[1] = 0; + p->vel[2] = -1; + + VectorClear( p->accel ); + + p->rotate = qfalse; + + p->roll = rand()%179; + + p->color = BLOODRED; + + p->alpha = 0.75; + + } + + +} + +void CG_ParticleSparks (vec3_t org, vec3_t vel, int duration, float x, float y, float speed) +{ + cparticle_t *p; + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + + p->endtime = cg.time + duration; + p->startfade = cg.time + duration/2; + + p->color = EMISIVEFADE; + p->alpha = 0.4f; + p->alphavel = 0; + + p->height = 0.5; + p->width = 0.5; + p->endheight = 0.5; + p->endwidth = 0.5; + + p->pshader = cgs.media.tracerShader; + + p->type = P_SMOKE; + + VectorCopy(org, p->org); + + p->org[0] += (crandom() * x); + p->org[1] += (crandom() * y); + + p->vel[0] = vel[0]; + p->vel[1] = vel[1]; + p->vel[2] = vel[2]; + + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + p->vel[0] += (crandom() * 4); + p->vel[1] += (crandom() * 4); + p->vel[2] += (20 + (crandom() * 10)) * speed; + + p->accel[0] = crandom () * 4; + p->accel[1] = crandom () * 4; + +} + +void CG_ParticleDust (centity_t *cent, vec3_t origin, vec3_t dir) +{ + float length; + float dist; + float crittersize; + vec3_t angles, forward; + vec3_t point; + cparticle_t *p; + int i; + + dist = 0; + + VectorNegate (dir, dir); + length = VectorLength (dir); + vectoangles (dir, angles); + AngleVectors (angles, forward, NULL, NULL); + + crittersize = LARGESIZE; + + if (length) + dist = length / crittersize; + + if (dist < 1) + dist = 1; + + VectorCopy (origin, point); + + for (i=0; inext; + p->next = active_particles; + active_particles = p; + + p->time = cg.time; + p->alpha = 5.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = cgs.media.smokePuffShader; + + // RF, stay around for long enough to expand and dissipate naturally + if (length) + p->endtime = cg.time + 4500 + (crandom() * 3500); + else + p->endtime = cg.time + 750 + (crandom() * 500); + + p->startfade = cg.time; + + p->width = LARGESIZE; + p->height = LARGESIZE; + + // RF, expand while falling + p->endheight = LARGESIZE*3.0; + p->endwidth = LARGESIZE*3.0; + + if (!length) + { + p->width *= 0.2f; + p->height *= 0.2f; + + p->endheight = NORMALSIZE; + p->endwidth = NORMALSIZE; + } + + p->type = P_SMOKE; + + VectorCopy( point, p->org ); + + p->vel[0] = crandom()*6; + p->vel[1] = crandom()*6; + p->vel[2] = random()*20; + + // RF, add some gravity/randomness + p->accel[0] = crandom()*3; + p->accel[1] = crandom()*3; + p->accel[2] = -PARTICLE_GRAVITY*0.4; + + VectorClear( p->accel ); + + p->rotate = qfalse; + + p->roll = rand()%179; + + p->alpha = 0.75; + + } + + +} + +void CG_ParticleMisc (qhandle_t pshader, vec3_t origin, int size, int duration, float alpha) +{ + cparticle_t *p; + + if (!pshader) + CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n"); + + if (!free_particles) + return; + + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->alpha = 1.0; + p->alphavel = 0; + p->roll = rand()%179; + + p->pshader = pshader; + + if (duration > 0) + p->endtime = cg.time + duration; + else + p->endtime = duration; + + p->startfade = cg.time; + + p->width = size; + p->height = size; + + p->endheight = size; + p->endwidth = size; + + p->type = P_SPRITE; + + VectorCopy( origin, p->org ); + + p->rotate = qfalse; +} + diff --git a/reaction/engine/code/cgame/cg_newdraw.c b/reaction/engine/code/cgame/cg_newdraw.c new file mode 100644 index 00000000..25dc3422 --- /dev/null +++ b/reaction/engine/code/cgame/cg_newdraw.c @@ -0,0 +1,1849 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#ifndef MISSIONPACK +#error This file not be used for classic Q3A. +#endif + +#include "cg_local.h" +#include "../ui/ui_shared.h" + +extern displayContextDef_t cgDC; + + +// set in CG_ParseTeamInfo + +//static int sortedTeamPlayers[TEAM_MAXOVERLAY]; +//static int numSortedTeamPlayers; +int drawTeamOverlayModificationCount = -1; + +//static char systemChat[256]; +//static char teamChat1[256]; +//static char teamChat2[256]; + +void CG_InitTeamChat(void) { + memset(teamChat1, 0, sizeof(teamChat1)); + memset(teamChat2, 0, sizeof(teamChat2)); + memset(systemChat, 0, sizeof(systemChat)); +} + +void CG_SetPrintString(int type, const char *p) { + if (type == SYSTEM_PRINT) { + strcpy(systemChat, p); + } else { + strcpy(teamChat2, teamChat1); + strcpy(teamChat1, p); + } +} + +void CG_CheckOrderPending(void) { + if (cgs.gametype < GT_CTF) { + return; + } + if (cgs.orderPending) { + //clientInfo_t *ci = cgs.clientinfo + sortedTeamPlayers[cg_currentSelectedPlayer.integer]; + const char *p1, *p2, *b; + p1 = p2 = b = NULL; + switch (cgs.currentOrder) { + case TEAMTASK_OFFENSE: + p1 = VOICECHAT_ONOFFENSE; + p2 = VOICECHAT_OFFENSE; + b = "+button7; wait; -button7"; + break; + case TEAMTASK_DEFENSE: + p1 = VOICECHAT_ONDEFENSE; + p2 = VOICECHAT_DEFEND; + b = "+button8; wait; -button8"; + break; + case TEAMTASK_PATROL: + p1 = VOICECHAT_ONPATROL; + p2 = VOICECHAT_PATROL; + b = "+button9; wait; -button9"; + break; + case TEAMTASK_FOLLOW: + p1 = VOICECHAT_ONFOLLOW; + p2 = VOICECHAT_FOLLOWME; + b = "+button10; wait; -button10"; + break; + case TEAMTASK_CAMP: + p1 = VOICECHAT_ONCAMPING; + p2 = VOICECHAT_CAMP; + break; + case TEAMTASK_RETRIEVE: + p1 = VOICECHAT_ONGETFLAG; + p2 = VOICECHAT_RETURNFLAG; + break; + case TEAMTASK_ESCORT: + p1 = VOICECHAT_ONFOLLOWCARRIER; + p2 = VOICECHAT_FOLLOWFLAGCARRIER; + break; + } + + if (cg_currentSelectedPlayer.integer == numSortedTeamPlayers) { + // to everyone + trap_SendConsoleCommand(va("cmd vsay_team %s\n", p2)); + } else { + // for the player self + if (sortedTeamPlayers[cg_currentSelectedPlayer.integer] == cg.snap->ps.clientNum && p1) { + trap_SendConsoleCommand(va("teamtask %i\n", cgs.currentOrder)); + //trap_SendConsoleCommand(va("cmd say_team %s\n", p2)); + trap_SendConsoleCommand(va("cmd vsay_team %s\n", p1)); + } else if (p2) { + //trap_SendConsoleCommand(va("cmd say_team %s, %s\n", ci->name,p)); + trap_SendConsoleCommand(va("cmd vtell %d %s\n", sortedTeamPlayers[cg_currentSelectedPlayer.integer], p2)); + } + } + if (b) { + trap_SendConsoleCommand(b); + } + cgs.orderPending = qfalse; + } +} + +static void CG_SetSelectedPlayerName( void ) { + if (cg_currentSelectedPlayer.integer >= 0 && cg_currentSelectedPlayer.integer < numSortedTeamPlayers) { + clientInfo_t *ci = cgs.clientinfo + sortedTeamPlayers[cg_currentSelectedPlayer.integer]; + if (ci) { + trap_Cvar_Set("cg_selectedPlayerName", ci->name); + trap_Cvar_Set("cg_selectedPlayer", va("%d", sortedTeamPlayers[cg_currentSelectedPlayer.integer])); + cgs.currentOrder = ci->teamTask; + } + } else { + trap_Cvar_Set("cg_selectedPlayerName", "Everyone"); + } +} +int CG_GetSelectedPlayer( void ) { + if (cg_currentSelectedPlayer.integer < 0 || cg_currentSelectedPlayer.integer >= numSortedTeamPlayers) { + cg_currentSelectedPlayer.integer = 0; + } + return cg_currentSelectedPlayer.integer; +} + +void CG_SelectNextPlayer( void ) { + CG_CheckOrderPending(); + if (cg_currentSelectedPlayer.integer >= 0 && cg_currentSelectedPlayer.integer < numSortedTeamPlayers) { + cg_currentSelectedPlayer.integer++; + } else { + cg_currentSelectedPlayer.integer = 0; + } + CG_SetSelectedPlayerName(); +} + +void CG_SelectPrevPlayer( void ) { + CG_CheckOrderPending(); + if (cg_currentSelectedPlayer.integer > 0 && cg_currentSelectedPlayer.integer < numSortedTeamPlayers) { + cg_currentSelectedPlayer.integer--; + } else { + cg_currentSelectedPlayer.integer = numSortedTeamPlayers; + } + CG_SetSelectedPlayerName(); +} + + +static void CG_DrawPlayerArmorIcon( rectDef_t *rect, qboolean draw2D ) { + centity_t *cent; + playerState_t *ps; + vec3_t angles; + vec3_t origin; + + if ( cg_drawStatus.integer == 0 ) { + return; + } + + cent = &cg_entities[cg.snap->ps.clientNum]; + ps = &cg.snap->ps; + + if ( draw2D || ( !cg_draw3dIcons.integer && cg_drawIcons.integer) ) { + CG_DrawPic( rect->x, rect->y + rect->h/2 + 1, rect->w, rect->h, cgs.media.armorIcon ); + } else if (cg_draw3dIcons.integer) { + VectorClear( angles ); + origin[0] = 90; + origin[1] = 0; + origin[2] = -10; + angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0; + + CG_Draw3DModel( rect->x, rect->y, rect->w, rect->h, cgs.media.armorModel, 0, origin, angles ); + } + +} + +static void CG_DrawPlayerArmorValue(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle) { + char num[16]; + int value; + centity_t *cent; + playerState_t *ps; + + cent = &cg_entities[cg.snap->ps.clientNum]; + ps = &cg.snap->ps; + + value = ps->stats[STAT_ARMOR]; + + + if (shader) { + trap_R_SetColor( color ); + CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader); + trap_R_SetColor( NULL ); + } else { + Com_sprintf (num, sizeof(num), "%i", value); + value = CG_Text_Width(num, scale, 0); + CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle); + } +} + +#ifndef MISSIONPACK +static float healthColors[4][4] = { +// { 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 }, {0.5, 0.5, 0.5, 1} }; + { 1.0f, 0.69f, 0.0f, 1.0f } , // normal + { 1.0f, 0.2f, 0.2f, 1.0f }, // low health + { 0.5f, 0.5f, 0.5f, 1.0f}, // weapon firing + { 1.0f, 1.0f, 1.0f, 1.0f } }; // health > 100 +#endif + +static void CG_DrawPlayerAmmoIcon( rectDef_t *rect, qboolean draw2D ) { + centity_t *cent; + playerState_t *ps; + vec3_t angles; + vec3_t origin; + + cent = &cg_entities[cg.snap->ps.clientNum]; + ps = &cg.snap->ps; + + if ( draw2D || (!cg_draw3dIcons.integer && cg_drawIcons.integer) ) { + qhandle_t icon; + icon = cg_weapons[ cg.predictedPlayerState.weapon ].ammoIcon; + if ( icon ) { + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, icon ); + } + } else if (cg_draw3dIcons.integer) { + if ( cent->currentState.weapon && cg_weapons[ cent->currentState.weapon ].ammoModel ) { + VectorClear( angles ); + origin[0] = 70; + origin[1] = 0; + origin[2] = 0; + angles[YAW] = 90 + 20 * sin( cg.time / 1000.0 ); + CG_Draw3DModel( rect->x, rect->y, rect->w, rect->h, cg_weapons[ cent->currentState.weapon ].ammoModel, 0, origin, angles ); + } + } +} + +static void CG_DrawPlayerAmmoValue(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle) { + char num[16]; + int value; + centity_t *cent; + playerState_t *ps; + + cent = &cg_entities[cg.snap->ps.clientNum]; + ps = &cg.snap->ps; + + if ( cent->currentState.weapon ) { + value = ps->ammo[cent->currentState.weapon]; + if ( value > -1 ) { + if (shader) { + trap_R_SetColor( color ); + CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader); + trap_R_SetColor( NULL ); + } else { + Com_sprintf (num, sizeof(num), "%i", value); + value = CG_Text_Width(num, scale, 0); + CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle); + } + } + } + +} + + + +static void CG_DrawPlayerHead(rectDef_t *rect, qboolean draw2D) { + vec3_t angles; + float size, stretch; + float frac; + float x = rect->x; + + VectorClear( angles ); + + if ( cg.damageTime && cg.time - cg.damageTime < DAMAGE_TIME ) { + frac = (float)(cg.time - cg.damageTime ) / DAMAGE_TIME; + size = rect->w * 1.25 * ( 1.5 - frac * 0.5 ); + + stretch = size - rect->w * 1.25; + // kick in the direction of damage + x -= stretch * 0.5 + cg.damageX * stretch * 0.5; + + cg.headStartYaw = 180 + cg.damageX * 45; + + cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI ); + cg.headEndPitch = 5 * cos( crandom()*M_PI ); + + cg.headStartTime = cg.time; + cg.headEndTime = cg.time + 100 + random() * 2000; + } else { + if ( cg.time >= cg.headEndTime ) { + // select a new head angle + cg.headStartYaw = cg.headEndYaw; + cg.headStartPitch = cg.headEndPitch; + cg.headStartTime = cg.headEndTime; + cg.headEndTime = cg.time + 100 + random() * 2000; + + cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI ); + cg.headEndPitch = 5 * cos( crandom()*M_PI ); + } + + size = rect->w * 1.25; + } + + // if the server was frozen for a while we may have a bad head start time + if ( cg.headStartTime > cg.time ) { + cg.headStartTime = cg.time; + } + + frac = ( cg.time - cg.headStartTime ) / (float)( cg.headEndTime - cg.headStartTime ); + frac = frac * frac * ( 3 - 2 * frac ); + angles[YAW] = cg.headStartYaw + ( cg.headEndYaw - cg.headStartYaw ) * frac; + angles[PITCH] = cg.headStartPitch + ( cg.headEndPitch - cg.headStartPitch ) * frac; + + CG_DrawHead( x, rect->y, rect->w, rect->h, cg.snap->ps.clientNum, angles ); +} + +static void CG_DrawSelectedPlayerHealth( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { + clientInfo_t *ci; + int value; + char num[16]; + + ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; + if (ci) { + if (shader) { + trap_R_SetColor( color ); + CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader); + trap_R_SetColor( NULL ); + } else { + Com_sprintf (num, sizeof(num), "%i", ci->health); + value = CG_Text_Width(num, scale, 0); + CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle); + } + } +} + +static void CG_DrawSelectedPlayerArmor( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { + clientInfo_t *ci; + int value; + char num[16]; + ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; + if (ci) { + if (ci->armor > 0) { + if (shader) { + trap_R_SetColor( color ); + CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader); + trap_R_SetColor( NULL ); + } else { + Com_sprintf (num, sizeof(num), "%i", ci->armor); + value = CG_Text_Width(num, scale, 0); + CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle); + } + } + } +} + +qhandle_t CG_StatusHandle(int task) { + qhandle_t h = cgs.media.assaultShader; + switch (task) { + case TEAMTASK_OFFENSE : + h = cgs.media.assaultShader; + break; + case TEAMTASK_DEFENSE : + h = cgs.media.defendShader; + break; + case TEAMTASK_PATROL : + h = cgs.media.patrolShader; + break; + case TEAMTASK_FOLLOW : + h = cgs.media.followShader; + break; + case TEAMTASK_CAMP : + h = cgs.media.campShader; + break; + case TEAMTASK_RETRIEVE : + h = cgs.media.retrieveShader; + break; + case TEAMTASK_ESCORT : + h = cgs.media.escortShader; + break; + default : + h = cgs.media.assaultShader; + break; + } + return h; +} + +static void CG_DrawSelectedPlayerStatus( rectDef_t *rect ) { + clientInfo_t *ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; + if (ci) { + qhandle_t h; + if (cgs.orderPending) { + // blink the icon + if ( cg.time > cgs.orderTime - 2500 && (cg.time >> 9 ) & 1 ) { + return; + } + h = CG_StatusHandle(cgs.currentOrder); + } else { + h = CG_StatusHandle(ci->teamTask); + } + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, h ); + } +} + + +static void CG_DrawPlayerStatus( rectDef_t *rect ) { + clientInfo_t *ci = &cgs.clientinfo[cg.snap->ps.clientNum]; + if (ci) { + qhandle_t h = CG_StatusHandle(ci->teamTask); + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, h); + } +} + + +static void CG_DrawSelectedPlayerName( rectDef_t *rect, float scale, vec4_t color, qboolean voice, int textStyle) { + clientInfo_t *ci; + ci = cgs.clientinfo + ((voice) ? cgs.currentVoiceClient : sortedTeamPlayers[CG_GetSelectedPlayer()]); + if (ci) { + CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, ci->name, 0, 0, textStyle); + } +} + +static void CG_DrawSelectedPlayerLocation( rectDef_t *rect, float scale, vec4_t color, int textStyle ) { + clientInfo_t *ci; + ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; + if (ci) { + const char *p = CG_ConfigString(CS_LOCATIONS + ci->location); + if (!p || !*p) { + p = "unknown"; + } + CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, p, 0, 0, textStyle); + } +} + +static void CG_DrawPlayerLocation( rectDef_t *rect, float scale, vec4_t color, int textStyle ) { + clientInfo_t *ci = &cgs.clientinfo[cg.snap->ps.clientNum]; + if (ci) { + const char *p = CG_ConfigString(CS_LOCATIONS + ci->location); + if (!p || !*p) { + p = "unknown"; + } + CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, p, 0, 0, textStyle); + } +} + + + +static void CG_DrawSelectedPlayerWeapon( rectDef_t *rect ) { + clientInfo_t *ci; + + ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; + if (ci) { + if ( cg_weapons[ci->curWeapon].weaponIcon ) { + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cg_weapons[ci->curWeapon].weaponIcon ); + } else { + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.deferShader); + } + } +} + +static void CG_DrawPlayerScore( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { + char num[16]; + int value = cg.snap->ps.persistant[PERS_SCORE]; + + if (shader) { + trap_R_SetColor( color ); + CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader); + trap_R_SetColor( NULL ); + } else { + Com_sprintf (num, sizeof(num), "%i", value); + value = CG_Text_Width(num, scale, 0); + CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle); + } +} + +static void CG_DrawPlayerItem( rectDef_t *rect, float scale, qboolean draw2D) { + int value; + vec3_t origin, angles; + + value = cg.snap->ps.stats[STAT_HOLDABLE_ITEM]; + if ( value ) { + CG_RegisterItemVisuals( value ); + + if (qtrue) { + CG_RegisterItemVisuals( value ); + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cg_items[ value ].icon ); + } else { + VectorClear( angles ); + origin[0] = 90; + origin[1] = 0; + origin[2] = -10; + angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0; + CG_Draw3DModel(rect->x, rect->y, rect->w, rect->h, cg_items[ value ].models[0], 0, origin, angles ); + } + } + +} + + +static void CG_DrawSelectedPlayerPowerup( rectDef_t *rect, qboolean draw2D ) { + clientInfo_t *ci; + int j; + float x, y; + + ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; + if (ci) { + x = rect->x; + y = rect->y; + + for (j = 0; j < PW_NUM_POWERUPS; j++) { + if (ci->powerups & (1 << j)) { + gitem_t *item; + item = BG_FindItemForPowerup( j ); + if (item) { + CG_DrawPic( x, y, rect->w, rect->h, trap_R_RegisterShader( item->icon ) ); + x += 3; + y += 3; + return; + } + } + } + + } +} + + +static void CG_DrawSelectedPlayerHead( rectDef_t *rect, qboolean draw2D, qboolean voice ) { + clipHandle_t cm; + clientInfo_t *ci; + float len; + vec3_t origin; + vec3_t mins, maxs, angles; + + + ci = cgs.clientinfo + ((voice) ? cgs.currentVoiceClient : sortedTeamPlayers[CG_GetSelectedPlayer()]); + + if (ci) { + if ( cg_draw3dIcons.integer ) { + cm = ci->headModel; + if ( !cm ) { + return; + } + + // offset the origin y and z to center the head + trap_R_ModelBounds( cm, mins, maxs ); + + origin[2] = -0.5 * ( mins[2] + maxs[2] ); + origin[1] = 0.5 * ( mins[1] + maxs[1] ); + + // calculate distance so the head nearly fills the box + // assume heads are taller than wide + len = 0.7 * ( maxs[2] - mins[2] ); + origin[0] = len / 0.268; // len / tan( fov/2 ) + + // allow per-model tweaking + VectorAdd( origin, ci->headOffset, origin ); + + angles[PITCH] = 0; + angles[YAW] = 180; + angles[ROLL] = 0; + + CG_Draw3DModel( rect->x, rect->y, rect->w, rect->h, ci->headModel, ci->headSkin, origin, angles ); + } else if ( cg_drawIcons.integer ) { + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, ci->modelIcon ); + } + + // if they are deferred, draw a cross out + if ( ci->deferred ) { + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.deferShader ); + } + } + +} + + +static void CG_DrawPlayerHealth(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { + playerState_t *ps; + int value; + char num[16]; + + ps = &cg.snap->ps; + + value = ps->stats[STAT_HEALTH]; + + if (shader) { + trap_R_SetColor( color ); + CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader); + trap_R_SetColor( NULL ); + } else { + Com_sprintf (num, sizeof(num), "%i", value); + value = CG_Text_Width(num, scale, 0); + CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle); + } +} + + +static void CG_DrawRedScore(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { + int value; + char num[16]; + if ( cgs.scores1 == SCORE_NOT_PRESENT ) { + Com_sprintf (num, sizeof(num), "-"); + } + else { + Com_sprintf (num, sizeof(num), "%i", cgs.scores1); + } + value = CG_Text_Width(num, scale, 0); + CG_Text_Paint(rect->x + rect->w - value, rect->y + rect->h, scale, color, num, 0, 0, textStyle); +} + +static void CG_DrawBlueScore(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { + int value; + char num[16]; + + if ( cgs.scores2 == SCORE_NOT_PRESENT ) { + Com_sprintf (num, sizeof(num), "-"); + } + else { + Com_sprintf (num, sizeof(num), "%i", cgs.scores2); + } + value = CG_Text_Width(num, scale, 0); + CG_Text_Paint(rect->x + rect->w - value, rect->y + rect->h, scale, color, num, 0, 0, textStyle); +} + +// FIXME: team name support +static void CG_DrawRedName(rectDef_t *rect, float scale, vec4_t color, int textStyle ) { + CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, cg_redTeamName.string , 0, 0, textStyle); +} + +static void CG_DrawBlueName(rectDef_t *rect, float scale, vec4_t color, int textStyle ) { + CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, cg_blueTeamName.string, 0, 0, textStyle); +} + +static void CG_DrawBlueFlagName(rectDef_t *rect, float scale, vec4_t color, int textStyle ) { + int i; + for ( i = 0 ; i < cgs.maxclients ; i++ ) { + if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_RED && cgs.clientinfo[i].powerups & ( 1<< PW_BLUEFLAG )) { + CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, cgs.clientinfo[i].name, 0, 0, textStyle); + return; + } + } +} + +static void CG_DrawBlueFlagStatus(rectDef_t *rect, qhandle_t shader) { + if (cgs.gametype != GT_CTF && cgs.gametype != GT_1FCTF) { + if (cgs.gametype == GT_HARVESTER) { + vec4_t color = {0, 0, 1, 1}; + trap_R_SetColor(color); + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.blueCubeIcon ); + trap_R_SetColor(NULL); + } + return; + } + if (shader) { + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); + } else { + gitem_t *item = BG_FindItemForPowerup( PW_BLUEFLAG ); + if (item) { + vec4_t color = {0, 0, 1, 1}; + trap_R_SetColor(color); + if( cgs.blueflag >= 0 && cgs.blueflag <= 2 ) { + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.flagShaders[cgs.blueflag] ); + } else { + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.flagShaders[0] ); + } + trap_R_SetColor(NULL); + } + } +} + +static void CG_DrawBlueFlagHead(rectDef_t *rect) { + int i; + for ( i = 0 ; i < cgs.maxclients ; i++ ) { + if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_RED && cgs.clientinfo[i].powerups & ( 1<< PW_BLUEFLAG )) { + vec3_t angles; + VectorClear( angles ); + angles[YAW] = 180 + 20 * sin( cg.time / 650.0 );; + CG_DrawHead( rect->x, rect->y, rect->w, rect->h, 0,angles ); + return; + } + } +} + +static void CG_DrawRedFlagName(rectDef_t *rect, float scale, vec4_t color, int textStyle ) { + int i; + for ( i = 0 ; i < cgs.maxclients ; i++ ) { + if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_BLUE && cgs.clientinfo[i].powerups & ( 1<< PW_REDFLAG )) { + CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, cgs.clientinfo[i].name, 0, 0, textStyle); + return; + } + } +} + +static void CG_DrawRedFlagStatus(rectDef_t *rect, qhandle_t shader) { + if (cgs.gametype != GT_CTF && cgs.gametype != GT_1FCTF) { + if (cgs.gametype == GT_HARVESTER) { + vec4_t color = {1, 0, 0, 1}; + trap_R_SetColor(color); + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.redCubeIcon ); + trap_R_SetColor(NULL); + } + return; + } + if (shader) { + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); + } else { + gitem_t *item = BG_FindItemForPowerup( PW_REDFLAG ); + if (item) { + vec4_t color = {1, 0, 0, 1}; + trap_R_SetColor(color); + if( cgs.redflag >= 0 && cgs.redflag <= 2) { + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.flagShaders[cgs.redflag] ); + } else { + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.flagShaders[0] ); + } + trap_R_SetColor(NULL); + } + } +} + +static void CG_DrawRedFlagHead(rectDef_t *rect) { + int i; + for ( i = 0 ; i < cgs.maxclients ; i++ ) { + if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_BLUE && cgs.clientinfo[i].powerups & ( 1<< PW_REDFLAG )) { + vec3_t angles; + VectorClear( angles ); + angles[YAW] = 180 + 20 * sin( cg.time / 650.0 );; + CG_DrawHead( rect->x, rect->y, rect->w, rect->h, 0,angles ); + return; + } + } +} + +static void CG_HarvesterSkulls(rectDef_t *rect, float scale, vec4_t color, qboolean force2D, int textStyle ) { + char num[16]; + vec3_t origin, angles; + qhandle_t handle; + int value = cg.snap->ps.generic1; + + if (cgs.gametype != GT_HARVESTER) { + return; + } + + if( value > 99 ) { + value = 99; + } + + Com_sprintf (num, sizeof(num), "%i", value); + value = CG_Text_Width(num, scale, 0); + CG_Text_Paint(rect->x + (rect->w - value), rect->y + rect->h, scale, color, num, 0, 0, textStyle); + + if (cg_drawIcons.integer) { + if (!force2D && cg_draw3dIcons.integer) { + VectorClear(angles); + origin[0] = 90; + origin[1] = 0; + origin[2] = -10; + angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0; + if( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) { + handle = cgs.media.redCubeModel; + } else { + handle = cgs.media.blueCubeModel; + } + CG_Draw3DModel( rect->x, rect->y, 35, 35, handle, 0, origin, angles ); + } else { + if( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) { + handle = cgs.media.redCubeIcon; + } else { + handle = cgs.media.blueCubeIcon; + } + CG_DrawPic( rect->x + 3, rect->y + 16, 20, 20, handle ); + } + } +} + +static void CG_OneFlagStatus(rectDef_t *rect) { + if (cgs.gametype != GT_1FCTF) { + return; + } else { + gitem_t *item = BG_FindItemForPowerup( PW_NEUTRALFLAG ); + if (item) { + if( cgs.flagStatus >= 0 && cgs.flagStatus <= 4 ) { + vec4_t color = {1, 1, 1, 1}; + int index = 0; + if (cgs.flagStatus == FLAG_TAKEN_RED) { + color[1] = color[2] = 0; + index = 1; + } else if (cgs.flagStatus == FLAG_TAKEN_BLUE) { + color[0] = color[1] = 0; + index = 1; + } else if (cgs.flagStatus == FLAG_DROPPED) { + index = 2; + } + trap_R_SetColor(color); + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.flagShaders[index] ); + } + } + } +} + + +static void CG_DrawCTFPowerUp(rectDef_t *rect) { + int value; + + if (cgs.gametype < GT_CTF) { + return; + } + value = cg.snap->ps.stats[STAT_PERSISTANT_POWERUP]; + if ( value ) { + CG_RegisterItemVisuals( value ); + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cg_items[ value ].icon ); + } +} + + + +static void CG_DrawTeamColor(rectDef_t *rect, vec4_t color) { + CG_DrawTeamBackground(rect->x, rect->y, rect->w, rect->h, color[3], cg.snap->ps.persistant[PERS_TEAM]); +} + +static void CG_DrawAreaPowerUp(rectDef_t *rect, int align, float special, float scale, vec4_t color) { + char num[16]; + int sorted[MAX_POWERUPS]; + int sortedTime[MAX_POWERUPS]; + int i, j, k; + int active; + playerState_t *ps; + int t; + gitem_t *item; + float f; + rectDef_t r2; + float *inc; + r2.x = rect->x; + r2.y = rect->y; + r2.w = rect->w; + r2.h = rect->h; + + inc = (align == HUD_VERTICAL) ? &r2.y : &r2.x; + + ps = &cg.snap->ps; + + if ( ps->stats[STAT_HEALTH] <= 0 ) { + return; + } + + // sort the list by time remaining + active = 0; + for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { + if ( !ps->powerups[ i ] ) { + continue; + } + t = ps->powerups[ i ] - cg.time; + // ZOID--don't draw if the power up has unlimited time (999 seconds) + // This is true of the CTF flags + if ( t <= 0 || t >= 999000) { + continue; + } + + // insert into the list + for ( j = 0 ; j < active ; j++ ) { + if ( sortedTime[j] >= t ) { + for ( k = active - 1 ; k >= j ; k-- ) { + sorted[k+1] = sorted[k]; + sortedTime[k+1] = sortedTime[k]; + } + break; + } + } + sorted[j] = i; + sortedTime[j] = t; + active++; + } + + // draw the icons and timers + for ( i = 0 ; i < active ; i++ ) { + item = BG_FindItemForPowerup( sorted[i] ); + + if (item) { + t = ps->powerups[ sorted[i] ]; + if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) { + trap_R_SetColor( NULL ); + } else { + vec4_t modulate; + + f = (float)( t - cg.time ) / POWERUP_BLINK_TIME; + f -= (int)f; + modulate[0] = modulate[1] = modulate[2] = modulate[3] = f; + trap_R_SetColor( modulate ); + } + + CG_DrawPic( r2.x, r2.y, r2.w * .75, r2.h, trap_R_RegisterShader( item->icon ) ); + + Com_sprintf (num, sizeof(num), "%i", sortedTime[i] / 1000); + CG_Text_Paint(r2.x + (r2.w * .75) + 3 , r2.y + r2.h, scale, color, num, 0, 0, 0); + *inc += r2.w + special; + } + + } + trap_R_SetColor( NULL ); + +} + +float CG_GetValue(int ownerDraw) { + centity_t *cent; + clientInfo_t *ci; + playerState_t *ps; + + cent = &cg_entities[cg.snap->ps.clientNum]; + ps = &cg.snap->ps; + + switch (ownerDraw) { + case CG_SELECTEDPLAYER_ARMOR: + ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; + return ci->armor; + break; + case CG_SELECTEDPLAYER_HEALTH: + ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; + return ci->health; + break; + case CG_PLAYER_ARMOR_VALUE: + return ps->stats[STAT_ARMOR]; + break; + case CG_PLAYER_AMMO_VALUE: + if ( cent->currentState.weapon ) { + return ps->ammo[cent->currentState.weapon]; + } + break; + case CG_PLAYER_SCORE: + return cg.snap->ps.persistant[PERS_SCORE]; + break; + case CG_PLAYER_HEALTH: + return ps->stats[STAT_HEALTH]; + break; + case CG_RED_SCORE: + return cgs.scores1; + break; + case CG_BLUE_SCORE: + return cgs.scores2; + break; + default: + break; + } + return -1; +} + +qboolean CG_OtherTeamHasFlag(void) { + if (cgs.gametype == GT_CTF || cgs.gametype == GT_1FCTF) { + int team = cg.snap->ps.persistant[PERS_TEAM]; + if (cgs.gametype == GT_1FCTF) { + if (team == TEAM_RED && cgs.flagStatus == FLAG_TAKEN_BLUE) { + return qtrue; + } else if (team == TEAM_BLUE && cgs.flagStatus == FLAG_TAKEN_RED) { + return qtrue; + } else { + return qfalse; + } + } else { + if (team == TEAM_RED && cgs.redflag == FLAG_TAKEN) { + return qtrue; + } else if (team == TEAM_BLUE && cgs.blueflag == FLAG_TAKEN) { + return qtrue; + } else { + return qfalse; + } + } + } + return qfalse; +} + +qboolean CG_YourTeamHasFlag(void) { + if (cgs.gametype == GT_CTF || cgs.gametype == GT_1FCTF) { + int team = cg.snap->ps.persistant[PERS_TEAM]; + if (cgs.gametype == GT_1FCTF) { + if (team == TEAM_RED && cgs.flagStatus == FLAG_TAKEN_RED) { + return qtrue; + } else if (team == TEAM_BLUE && cgs.flagStatus == FLAG_TAKEN_BLUE) { + return qtrue; + } else { + return qfalse; + } + } else { + if (team == TEAM_RED && cgs.blueflag == FLAG_TAKEN) { + return qtrue; + } else if (team == TEAM_BLUE && cgs.redflag == FLAG_TAKEN) { + return qtrue; + } else { + return qfalse; + } + } + } + return qfalse; +} + +// THINKABOUTME: should these be exclusive or inclusive.. +// +qboolean CG_OwnerDrawVisible(int flags) { + + if (flags & CG_SHOW_TEAMINFO) { + return (cg_currentSelectedPlayer.integer == numSortedTeamPlayers); + } + + if (flags & CG_SHOW_NOTEAMINFO) { + return !(cg_currentSelectedPlayer.integer == numSortedTeamPlayers); + } + + if (flags & CG_SHOW_OTHERTEAMHASFLAG) { + return CG_OtherTeamHasFlag(); + } + + if (flags & CG_SHOW_YOURTEAMHASENEMYFLAG) { + return CG_YourTeamHasFlag(); + } + + if (flags & (CG_SHOW_BLUE_TEAM_HAS_REDFLAG | CG_SHOW_RED_TEAM_HAS_BLUEFLAG)) { + if (flags & CG_SHOW_BLUE_TEAM_HAS_REDFLAG && (cgs.redflag == FLAG_TAKEN || cgs.flagStatus == FLAG_TAKEN_RED)) { + return qtrue; + } else if (flags & CG_SHOW_RED_TEAM_HAS_BLUEFLAG && (cgs.blueflag == FLAG_TAKEN || cgs.flagStatus == FLAG_TAKEN_BLUE)) { + return qtrue; + } + return qfalse; + } + + if (flags & CG_SHOW_ANYTEAMGAME) { + if( cgs.gametype >= GT_TEAM) { + return qtrue; + } + } + + if (flags & CG_SHOW_ANYNONTEAMGAME) { + if( cgs.gametype < GT_TEAM) { + return qtrue; + } + } + + if (flags & CG_SHOW_HARVESTER) { + if( cgs.gametype == GT_HARVESTER ) { + return qtrue; + } else { + return qfalse; + } + } + + if (flags & CG_SHOW_ONEFLAG) { + if( cgs.gametype == GT_1FCTF ) { + return qtrue; + } else { + return qfalse; + } + } + + if (flags & CG_SHOW_CTF) { + if( cgs.gametype == GT_CTF ) { + return qtrue; + } + } + + if (flags & CG_SHOW_OBELISK) { + if( cgs.gametype == GT_OBELISK ) { + return qtrue; + } else { + return qfalse; + } + } + + if (flags & CG_SHOW_HEALTHCRITICAL) { + if (cg.snap->ps.stats[STAT_HEALTH] < 25) { + return qtrue; + } + } + + if (flags & CG_SHOW_HEALTHOK) { + if (cg.snap->ps.stats[STAT_HEALTH] >= 25) { + return qtrue; + } + } + + if (flags & CG_SHOW_SINGLEPLAYER) { + if( cgs.gametype == GT_SINGLE_PLAYER ) { + return qtrue; + } + } + + if (flags & CG_SHOW_TOURNAMENT) { + if( cgs.gametype == GT_TOURNAMENT ) { + return qtrue; + } + } + + if (flags & CG_SHOW_DURINGINCOMINGVOICE) { + } + + if (flags & CG_SHOW_IF_PLAYER_HAS_FLAG) { + if (cg.snap->ps.powerups[PW_REDFLAG] || cg.snap->ps.powerups[PW_BLUEFLAG] || cg.snap->ps.powerups[PW_NEUTRALFLAG]) { + return qtrue; + } + } + return qfalse; +} + + + +static void CG_DrawPlayerHasFlag(rectDef_t *rect, qboolean force2D) { + int adj = (force2D) ? 0 : 2; + if( cg.predictedPlayerState.powerups[PW_REDFLAG] ) { + CG_DrawFlagModel( rect->x + adj, rect->y + adj, rect->w - adj, rect->h - adj, TEAM_RED, force2D); + } else if( cg.predictedPlayerState.powerups[PW_BLUEFLAG] ) { + CG_DrawFlagModel( rect->x + adj, rect->y + adj, rect->w - adj, rect->h - adj, TEAM_BLUE, force2D); + } else if( cg.predictedPlayerState.powerups[PW_NEUTRALFLAG] ) { + CG_DrawFlagModel( rect->x + adj, rect->y + adj, rect->w - adj, rect->h - adj, TEAM_FREE, force2D); + } +} + +static void CG_DrawAreaSystemChat(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader) { + CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, systemChat, 0, 0, 0); +} + +static void CG_DrawAreaTeamChat(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader) { + CG_Text_Paint(rect->x, rect->y + rect->h, scale, color,teamChat1, 0, 0, 0); +} + +static void CG_DrawAreaChat(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader) { + CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, teamChat2, 0, 0, 0); +} + +const char *CG_GetKillerText(void) { + const char *s = ""; + if ( cg.killerName[0] ) { + s = va("Fragged by %s", cg.killerName ); + } + return s; +} + + +static void CG_DrawKiller(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { + // fragged by ... line + if ( cg.killerName[0] ) { + int x = rect->x + rect->w / 2; + CG_Text_Paint(x - CG_Text_Width(CG_GetKillerText(), scale, 0) / 2, rect->y + rect->h, scale, color, CG_GetKillerText(), 0, 0, textStyle); + } + +} + + +static void CG_DrawCapFragLimit(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle) { + int limit = (cgs.gametype >= GT_CTF) ? cgs.capturelimit : cgs.fraglimit; + CG_Text_Paint(rect->x, rect->y, scale, color, va("%2i", limit),0, 0, textStyle); +} + +static void CG_Draw1stPlace(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle) { + if (cgs.scores1 != SCORE_NOT_PRESENT) { + CG_Text_Paint(rect->x, rect->y, scale, color, va("%2i", cgs.scores1),0, 0, textStyle); + } +} + +static void CG_Draw2ndPlace(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle) { + if (cgs.scores2 != SCORE_NOT_PRESENT) { + CG_Text_Paint(rect->x, rect->y, scale, color, va("%2i", cgs.scores2),0, 0, textStyle); + } +} + +const char *CG_GetGameStatusText(void) { + const char *s = ""; + if ( cgs.gametype < GT_TEAM) { + if (cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) { + s = va("%s place with %i",CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ),cg.snap->ps.persistant[PERS_SCORE] ); + } + } else { + if ( cg.teamScores[0] == cg.teamScores[1] ) { + s = va("Teams are tied at %i", cg.teamScores[0] ); + } else if ( cg.teamScores[0] >= cg.teamScores[1] ) { + s = va("Red leads Blue, %i to %i", cg.teamScores[0], cg.teamScores[1] ); + } else { + s = va("Blue leads Red, %i to %i", cg.teamScores[1], cg.teamScores[0] ); + } + } + return s; +} + +static void CG_DrawGameStatus(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { + CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, CG_GetGameStatusText(), 0, 0, textStyle); +} + +const char *CG_GameTypeString(void) { + if ( cgs.gametype == GT_FFA ) { + return "Free For All"; + } else if ( cgs.gametype == GT_TEAM ) { + return "Team Deathmatch"; + } else if ( cgs.gametype == GT_CTF ) { + return "Capture the Flag"; + } else if ( cgs.gametype == GT_1FCTF ) { + return "One Flag CTF"; + } else if ( cgs.gametype == GT_OBELISK ) { + return "Overload"; + } else if ( cgs.gametype == GT_HARVESTER ) { + return "Harvester"; + } + return ""; +} +static void CG_DrawGameType(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { + CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, CG_GameTypeString(), 0, 0, textStyle); +} + +static void CG_Text_Paint_Limit(float *maxX, float x, float y, float scale, vec4_t color, const char* text, float adjust, int limit) { + int len, count; + vec4_t newColor; + glyphInfo_t *glyph; + if (text) { + const char *s = text; + float max = *maxX; + float useScale; + fontInfo_t *font = &cgDC.Assets.textFont; + if (scale <= cg_smallFont.value) { + font = &cgDC.Assets.smallFont; + } else if (scale > cg_bigFont.value) { + font = &cgDC.Assets.bigFont; + } + useScale = scale * font->glyphScale; + trap_R_SetColor( color ); + len = strlen(text); + if (limit > 0 && len > limit) { + len = limit; + } + count = 0; + while (s && *s && count < len) { + glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build + if ( Q_IsColorString( s ) ) { + memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) ); + newColor[3] = color[3]; + trap_R_SetColor( newColor ); + s += 2; + continue; + } else { + float yadj = useScale * glyph->top; + if (CG_Text_Width(s, useScale, 1) + x > max) { + *maxX = 0; + break; + } + CG_Text_PaintChar(x, y - yadj, + glyph->imageWidth, + glyph->imageHeight, + useScale, + glyph->s, + glyph->t, + glyph->s2, + glyph->t2, + glyph->glyph); + x += (glyph->xSkip * useScale) + adjust; + *maxX = x; + count++; + s++; + } + } + trap_R_SetColor( NULL ); + } + +} + + + +#define PIC_WIDTH 12 + +void CG_DrawNewTeamInfo(rectDef_t *rect, float text_x, float text_y, float scale, vec4_t color, qhandle_t shader) { + int xx; + float y; + int i, j, len, count; + const char *p; + vec4_t hcolor; + float pwidth, lwidth, maxx, leftOver; + clientInfo_t *ci; + gitem_t *item; + qhandle_t h; + + // max player name width + pwidth = 0; + count = (numSortedTeamPlayers > 8) ? 8 : numSortedTeamPlayers; + for (i = 0; i < count; i++) { + ci = cgs.clientinfo + sortedTeamPlayers[i]; + if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) { + len = CG_Text_Width( ci->name, scale, 0); + if (len > pwidth) + pwidth = len; + } + } + + // max location name width + lwidth = 0; + for (i = 1; i < MAX_LOCATIONS; i++) { + p = CG_ConfigString(CS_LOCATIONS + i); + if (p && *p) { + len = CG_Text_Width(p, scale, 0); + if (len > lwidth) + lwidth = len; + } + } + + y = rect->y; + + for (i = 0; i < count; i++) { + ci = cgs.clientinfo + sortedTeamPlayers[i]; + if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) { + + xx = rect->x + 1; + for (j = 0; j <= PW_NUM_POWERUPS; j++) { + if (ci->powerups & (1 << j)) { + + item = BG_FindItemForPowerup( j ); + + if (item) { + CG_DrawPic( xx, y, PIC_WIDTH, PIC_WIDTH, trap_R_RegisterShader( item->icon ) ); + xx += PIC_WIDTH; + } + } + } + + // FIXME: max of 3 powerups shown properly + xx = rect->x + (PIC_WIDTH * 3) + 2; + + CG_GetColorForHealth( ci->health, ci->armor, hcolor ); + trap_R_SetColor(hcolor); + CG_DrawPic( xx, y + 1, PIC_WIDTH - 2, PIC_WIDTH - 2, cgs.media.heartShader ); + + //Com_sprintf (st, sizeof(st), "%3i %3i", ci->health, ci->armor); + //CG_Text_Paint(xx, y + text_y, scale, hcolor, st, 0, 0); + + // draw weapon icon + xx += PIC_WIDTH + 1; + +// weapon used is not that useful, use the space for task +#if 0 + if ( cg_weapons[ci->curWeapon].weaponIcon ) { + CG_DrawPic( xx, y, PIC_WIDTH, PIC_WIDTH, cg_weapons[ci->curWeapon].weaponIcon ); + } else { + CG_DrawPic( xx, y, PIC_WIDTH, PIC_WIDTH, cgs.media.deferShader ); + } +#endif + + trap_R_SetColor(NULL); + if (cgs.orderPending) { + // blink the icon + if ( cg.time > cgs.orderTime - 2500 && (cg.time >> 9 ) & 1 ) { + h = 0; + } else { + h = CG_StatusHandle(cgs.currentOrder); + } + } else { + h = CG_StatusHandle(ci->teamTask); + } + + if (h) { + CG_DrawPic( xx, y, PIC_WIDTH, PIC_WIDTH, h); + } + + xx += PIC_WIDTH + 1; + + leftOver = rect->w - xx; + maxx = xx + leftOver / 3; + + + + CG_Text_Paint_Limit(&maxx, xx, y + text_y, scale, color, ci->name, 0, 0); + + p = CG_ConfigString(CS_LOCATIONS + ci->location); + if (!p || !*p) { + p = "unknown"; + } + + xx += leftOver / 3 + 2; + maxx = rect->w - 4; + + CG_Text_Paint_Limit(&maxx, xx, y + text_y, scale, color, p, 0, 0); + y += text_y + 2; + if ( y + text_y + 2 > rect->y + rect->h ) { + break; + } + + } + } +} + + +void CG_DrawTeamSpectators(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader) { + if (cg.spectatorLen) { + float maxX; + + if (cg.spectatorWidth == -1) { + cg.spectatorWidth = 0; + cg.spectatorPaintX = rect->x + 1; + cg.spectatorPaintX2 = -1; + } + + if (cg.spectatorOffset > cg.spectatorLen) { + cg.spectatorOffset = 0; + cg.spectatorPaintX = rect->x + 1; + cg.spectatorPaintX2 = -1; + } + + if (cg.time > cg.spectatorTime) { + cg.spectatorTime = cg.time + 10; + if (cg.spectatorPaintX <= rect->x + 2) { + if (cg.spectatorOffset < cg.spectatorLen) { + cg.spectatorPaintX += CG_Text_Width(&cg.spectatorList[cg.spectatorOffset], scale, 1) - 1; + cg.spectatorOffset++; + } else { + cg.spectatorOffset = 0; + if (cg.spectatorPaintX2 >= 0) { + cg.spectatorPaintX = cg.spectatorPaintX2; + } else { + cg.spectatorPaintX = rect->x + rect->w - 2; + } + cg.spectatorPaintX2 = -1; + } + } else { + cg.spectatorPaintX--; + if (cg.spectatorPaintX2 >= 0) { + cg.spectatorPaintX2--; + } + } + } + + maxX = rect->x + rect->w - 2; + CG_Text_Paint_Limit(&maxX, cg.spectatorPaintX, rect->y + rect->h - 3, scale, color, &cg.spectatorList[cg.spectatorOffset], 0, 0); + if (cg.spectatorPaintX2 >= 0) { + float maxX2 = rect->x + rect->w - 2; + CG_Text_Paint_Limit(&maxX2, cg.spectatorPaintX2, rect->y + rect->h - 3, scale, color, cg.spectatorList, 0, cg.spectatorOffset); + } + if (cg.spectatorOffset && maxX > 0) { + // if we have an offset ( we are skipping the first part of the string ) and we fit the string + if (cg.spectatorPaintX2 == -1) { + cg.spectatorPaintX2 = rect->x + rect->w - 2; + } + } else { + cg.spectatorPaintX2 = -1; + } + + } +} + + + +void CG_DrawMedal(int ownerDraw, rectDef_t *rect, float scale, vec4_t color, qhandle_t shader) { + score_t *score = &cg.scores[cg.selectedScore]; + float value = 0; + char *text = NULL; + color[3] = 0.25; + + switch (ownerDraw) { + case CG_ACCURACY: + value = score->accuracy; + break; + case CG_ASSISTS: + value = score->assistCount; + break; + case CG_DEFEND: + value = score->defendCount; + break; + case CG_EXCELLENT: + value = score->excellentCount; + break; + case CG_IMPRESSIVE: + value = score->impressiveCount; + break; + case CG_PERFECT: + value = score->perfect; + break; + case CG_GAUNTLET: + value = score->guantletCount; + break; + case CG_CAPTURES: + value = score->captures; + break; + } + + if (value > 0) { + if (ownerDraw != CG_PERFECT) { + if (ownerDraw == CG_ACCURACY) { + text = va("%i%%", (int)value); + if (value > 50) { + color[3] = 1.0; + } + } else { + text = va("%i", (int)value); + color[3] = 1.0; + } + } else { + if (value) { + color[3] = 1.0; + } + text = "Wow"; + } + } + + trap_R_SetColor(color); + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); + + if (text) { + color[3] = 1.0; + value = CG_Text_Width(text, scale, 0); + CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h + 10 , scale, color, text, 0, 0, 0); + } + trap_R_SetColor(NULL); + +} + + +// +void CG_OwnerDraw(float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader, int textStyle) { + rectDef_t rect; + + if ( cg_drawStatus.integer == 0 ) { + return; + } + + //if (ownerDrawFlags != 0 && !CG_OwnerDrawVisible(ownerDrawFlags)) { + // return; + //} + + rect.x = x; + rect.y = y; + rect.w = w; + rect.h = h; + + switch (ownerDraw) { + case CG_PLAYER_ARMOR_ICON: + CG_DrawPlayerArmorIcon(&rect, ownerDrawFlags & CG_SHOW_2DONLY); + break; + case CG_PLAYER_ARMOR_ICON2D: + CG_DrawPlayerArmorIcon(&rect, qtrue); + break; + case CG_PLAYER_ARMOR_VALUE: + CG_DrawPlayerArmorValue(&rect, scale, color, shader, textStyle); + break; + case CG_PLAYER_AMMO_ICON: + CG_DrawPlayerAmmoIcon(&rect, ownerDrawFlags & CG_SHOW_2DONLY); + break; + case CG_PLAYER_AMMO_ICON2D: + CG_DrawPlayerAmmoIcon(&rect, qtrue); + break; + case CG_PLAYER_AMMO_VALUE: + CG_DrawPlayerAmmoValue(&rect, scale, color, shader, textStyle); + break; + case CG_SELECTEDPLAYER_HEAD: + CG_DrawSelectedPlayerHead(&rect, ownerDrawFlags & CG_SHOW_2DONLY, qfalse); + break; + case CG_VOICE_HEAD: + CG_DrawSelectedPlayerHead(&rect, ownerDrawFlags & CG_SHOW_2DONLY, qtrue); + break; + case CG_VOICE_NAME: + CG_DrawSelectedPlayerName(&rect, scale, color, qtrue, textStyle); + break; + case CG_SELECTEDPLAYER_STATUS: + CG_DrawSelectedPlayerStatus(&rect); + break; + case CG_SELECTEDPLAYER_ARMOR: + CG_DrawSelectedPlayerArmor(&rect, scale, color, shader, textStyle); + break; + case CG_SELECTEDPLAYER_HEALTH: + CG_DrawSelectedPlayerHealth(&rect, scale, color, shader, textStyle); + break; + case CG_SELECTEDPLAYER_NAME: + CG_DrawSelectedPlayerName(&rect, scale, color, qfalse, textStyle); + break; + case CG_SELECTEDPLAYER_LOCATION: + CG_DrawSelectedPlayerLocation(&rect, scale, color, textStyle); + break; + case CG_SELECTEDPLAYER_WEAPON: + CG_DrawSelectedPlayerWeapon(&rect); + break; + case CG_SELECTEDPLAYER_POWERUP: + CG_DrawSelectedPlayerPowerup(&rect, ownerDrawFlags & CG_SHOW_2DONLY); + break; + case CG_PLAYER_HEAD: + CG_DrawPlayerHead(&rect, ownerDrawFlags & CG_SHOW_2DONLY); + break; + case CG_PLAYER_ITEM: + CG_DrawPlayerItem(&rect, scale, ownerDrawFlags & CG_SHOW_2DONLY); + break; + case CG_PLAYER_SCORE: + CG_DrawPlayerScore(&rect, scale, color, shader, textStyle); + break; + case CG_PLAYER_HEALTH: + CG_DrawPlayerHealth(&rect, scale, color, shader, textStyle); + break; + case CG_RED_SCORE: + CG_DrawRedScore(&rect, scale, color, shader, textStyle); + break; + case CG_BLUE_SCORE: + CG_DrawBlueScore(&rect, scale, color, shader, textStyle); + break; + case CG_RED_NAME: + CG_DrawRedName(&rect, scale, color, textStyle); + break; + case CG_BLUE_NAME: + CG_DrawBlueName(&rect, scale, color, textStyle); + break; + case CG_BLUE_FLAGHEAD: + CG_DrawBlueFlagHead(&rect); + break; + case CG_BLUE_FLAGSTATUS: + CG_DrawBlueFlagStatus(&rect, shader); + break; + case CG_BLUE_FLAGNAME: + CG_DrawBlueFlagName(&rect, scale, color, textStyle); + break; + case CG_RED_FLAGHEAD: + CG_DrawRedFlagHead(&rect); + break; + case CG_RED_FLAGSTATUS: + CG_DrawRedFlagStatus(&rect, shader); + break; + case CG_RED_FLAGNAME: + CG_DrawRedFlagName(&rect, scale, color, textStyle); + break; + case CG_HARVESTER_SKULLS: + CG_HarvesterSkulls(&rect, scale, color, qfalse, textStyle); + break; + case CG_HARVESTER_SKULLS2D: + CG_HarvesterSkulls(&rect, scale, color, qtrue, textStyle); + break; + case CG_ONEFLAG_STATUS: + CG_OneFlagStatus(&rect); + break; + case CG_PLAYER_LOCATION: + CG_DrawPlayerLocation(&rect, scale, color, textStyle); + break; + case CG_TEAM_COLOR: + CG_DrawTeamColor(&rect, color); + break; + case CG_CTF_POWERUP: + CG_DrawCTFPowerUp(&rect); + break; + case CG_AREA_POWERUP: + CG_DrawAreaPowerUp(&rect, align, special, scale, color); + break; + case CG_PLAYER_STATUS: + CG_DrawPlayerStatus(&rect); + break; + case CG_PLAYER_HASFLAG: + CG_DrawPlayerHasFlag(&rect, qfalse); + break; + case CG_PLAYER_HASFLAG2D: + CG_DrawPlayerHasFlag(&rect, qtrue); + break; + case CG_AREA_SYSTEMCHAT: + CG_DrawAreaSystemChat(&rect, scale, color, shader); + break; + case CG_AREA_TEAMCHAT: + CG_DrawAreaTeamChat(&rect, scale, color, shader); + break; + case CG_AREA_CHAT: + CG_DrawAreaChat(&rect, scale, color, shader); + break; + case CG_GAME_TYPE: + CG_DrawGameType(&rect, scale, color, shader, textStyle); + break; + case CG_GAME_STATUS: + CG_DrawGameStatus(&rect, scale, color, shader, textStyle); + break; + case CG_KILLER: + CG_DrawKiller(&rect, scale, color, shader, textStyle); + break; + case CG_ACCURACY: + case CG_ASSISTS: + case CG_DEFEND: + case CG_EXCELLENT: + case CG_IMPRESSIVE: + case CG_PERFECT: + case CG_GAUNTLET: + case CG_CAPTURES: + CG_DrawMedal(ownerDraw, &rect, scale, color, shader); + break; + case CG_SPECTATORS: + CG_DrawTeamSpectators(&rect, scale, color, shader); + break; + case CG_TEAMINFO: + if (cg_currentSelectedPlayer.integer == numSortedTeamPlayers) { + CG_DrawNewTeamInfo(&rect, text_x, text_y, scale, color, shader); + } + break; + case CG_CAPFRAGLIMIT: + CG_DrawCapFragLimit(&rect, scale, color, shader, textStyle); + break; + case CG_1STPLACE: + CG_Draw1stPlace(&rect, scale, color, shader, textStyle); + break; + case CG_2NDPLACE: + CG_Draw2ndPlace(&rect, scale, color, shader, textStyle); + break; + default: + break; + } +} + +void CG_MouseEvent(int x, int y) { + int n; + + if ( (cg.predictedPlayerState.pm_type == PM_NORMAL || cg.predictedPlayerState.pm_type == PM_SPECTATOR) && cg.showScores == qfalse) { + trap_Key_SetCatcher(0); + return; + } + + cgs.cursorX+= x; + if (cgs.cursorX < 0) + cgs.cursorX = 0; + else if (cgs.cursorX > 640) + cgs.cursorX = 640; + + cgs.cursorY += y; + if (cgs.cursorY < 0) + cgs.cursorY = 0; + else if (cgs.cursorY > 480) + cgs.cursorY = 480; + + n = Display_CursorType(cgs.cursorX, cgs.cursorY); + cgs.activeCursor = 0; + if (n == CURSOR_ARROW) { + cgs.activeCursor = cgs.media.selectCursor; + } else if (n == CURSOR_SIZER) { + cgs.activeCursor = cgs.media.sizeCursor; + } + + if (cgs.capturedItem) { + Display_MouseMove(cgs.capturedItem, x, y); + } else { + Display_MouseMove(NULL, cgs.cursorX, cgs.cursorY); + } + +} + +/* +================== +CG_HideTeamMenus +================== + +*/ +void CG_HideTeamMenu( void ) { + Menus_CloseByName("teamMenu"); + Menus_CloseByName("getMenu"); +} + +/* +================== +CG_ShowTeamMenus +================== + +*/ +void CG_ShowTeamMenu( void ) { + Menus_OpenByName("teamMenu"); +} + + + + +/* +================== +CG_EventHandling +================== + type 0 - no event handling + 1 - team menu + 2 - hud editor + +*/ +void CG_EventHandling(int type) { + cgs.eventHandling = type; + if (type == CGAME_EVENT_NONE) { + CG_HideTeamMenu(); + } else if (type == CGAME_EVENT_TEAMMENU) { + //CG_ShowTeamMenu(); + } else if (type == CGAME_EVENT_SCOREBOARD) { + } + +} + + + +void CG_KeyEvent(int key, qboolean down) { + + if (!down) { + return; + } + + if ( cg.predictedPlayerState.pm_type == PM_NORMAL || (cg.predictedPlayerState.pm_type == PM_SPECTATOR && cg.showScores == qfalse)) { + CG_EventHandling(CGAME_EVENT_NONE); + trap_Key_SetCatcher(0); + return; + } + + //if (key == trap_Key_GetKey("teamMenu") || !Display_CaptureItem(cgs.cursorX, cgs.cursorY)) { + // if we see this then we should always be visible + // CG_EventHandling(CGAME_EVENT_NONE); + // trap_Key_SetCatcher(0); + //} + + + + Display_HandleKey(key, down, cgs.cursorX, cgs.cursorY); + + if (cgs.capturedItem) { + cgs.capturedItem = NULL; + } else { + if (key == K_MOUSE2 && down) { + cgs.capturedItem = Display_CaptureItem(cgs.cursorX, cgs.cursorY); + } + } +} + +int CG_ClientNumFromName(const char *p) { + int i; + for (i = 0; i < cgs.maxclients; i++) { + if (cgs.clientinfo[i].infoValid && Q_stricmp(cgs.clientinfo[i].name, p) == 0) { + return i; + } + } + return -1; +} + +void CG_ShowResponseHead(void) { + Menus_OpenByName("voiceMenu"); + trap_Cvar_Set("cl_conXOffset", "72"); + cg.voiceTime = cg.time; +} + +void CG_RunMenuScript(char **args) { +} + + +void CG_GetTeamColor(vec4_t *color) { + if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED) { + (*color)[0] = 1.0f; + (*color)[3] = 0.25f; + (*color)[1] = (*color)[2] = 0.0f; + } else if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE) { + (*color)[0] = (*color)[1] = 0.0f; + (*color)[2] = 1.0f; + (*color)[3] = 0.25f; + } else { + (*color)[0] = (*color)[2] = 0.0f; + (*color)[1] = 0.17f; + (*color)[3] = 0.25f; + } +} + diff --git a/reaction/engine/code/cgame/cg_particles.c b/reaction/engine/code/cgame/cg_particles.c new file mode 100644 index 00000000..72c077f5 --- /dev/null +++ b/reaction/engine/code/cgame/cg_particles.c @@ -0,0 +1,2018 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// Rafael particles +// cg_particles.c + +#include "cg_local.h" + +#define BLOODRED 2 +#define EMISIVEFADE 3 +#define GREY75 4 + +typedef struct particle_s +{ + struct particle_s *next; + + float time; + float endtime; + + vec3_t org; + vec3_t vel; + vec3_t accel; + int color; + float colorvel; + float alpha; + float alphavel; + int type; + qhandle_t pshader; + + float height; + float width; + + float endheight; + float endwidth; + + float start; + float end; + + float startfade; + qboolean rotate; + int snum; + + qboolean link; + + // Ridah + int shaderAnim; + int roll; + + int accumroll; + +} cparticle_t; + +typedef enum +{ + P_NONE, + P_WEATHER, + P_FLAT, + P_SMOKE, + P_ROTATE, + P_WEATHER_TURBULENT, + P_ANIM, // Ridah + P_BAT, + P_BLEED, + P_FLAT_SCALEUP, + P_FLAT_SCALEUP_FADE, + P_WEATHER_FLURRY, + P_SMOKE_IMPACT, + P_BUBBLE, + P_BUBBLE_TURBULENT, + P_SPRITE +} particle_type_t; + +#define MAX_SHADER_ANIMS 32 +#define MAX_SHADER_ANIM_FRAMES 64 + +static char *shaderAnimNames[MAX_SHADER_ANIMS] = { + "explode1", + "blacksmokeanim", + "twiltb2", + "expblue", + "blacksmokeanimb", // uses 'explode1' sequence + "blood", + NULL +}; +static qhandle_t shaderAnims[MAX_SHADER_ANIMS][MAX_SHADER_ANIM_FRAMES]; +static int shaderAnimCounts[MAX_SHADER_ANIMS] = { + 23, + 25, + 45, + 25, + 23, + 5, +}; +static float shaderAnimSTRatio[MAX_SHADER_ANIMS] = { + 1.405f, + 1.0f, + 1.0f, + 1.0f, + 1.0f, + 1.0f, +}; +static int numShaderAnims; +// done. + +#define PARTICLE_GRAVITY 40 +#define MAX_PARTICLES 1024 * 8 + +cparticle_t *active_particles, *free_particles; +cparticle_t particles[MAX_PARTICLES]; +int cl_numparticles = MAX_PARTICLES; + +qboolean initparticles = qfalse; +vec3_t vforward, vright, vup; +vec3_t rforward, rright, rup; + +float oldtime; + +/* +=============== +CL_ClearParticles +=============== +*/ +void CG_ClearParticles (void) +{ + int i; + + memset( particles, 0, sizeof(particles) ); + + free_particles = &particles[0]; + active_particles = NULL; + + for (i=0 ;itype == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY + || p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) + {// create a front facing polygon + + if (p->type != P_WEATHER_FLURRY) + { + if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) + { + if (org[2] > p->end) + { + p->time = cg.time; + VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground + + p->org[2] = ( p->start + crandom () * 4 ); + + + if (p->type == P_BUBBLE_TURBULENT) + { + p->vel[0] = crandom() * 4; + p->vel[1] = crandom() * 4; + } + + } + } + else + { + if (org[2] < p->end) + { + p->time = cg.time; + VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground + + while (p->org[2] < p->end) + { + p->org[2] += (p->start - p->end); + } + + + if (p->type == P_WEATHER_TURBULENT) + { + p->vel[0] = crandom() * 16; + p->vel[1] = crandom() * 16; + } + + } + } + + + // Rafael snow pvs check + if (!p->link) + return; + + p->alpha = 1; + } + + // Ridah, had to do this or MAX_POLYS is being exceeded in village1.bsp + if (Distance( cg.snap->ps.origin, org ) > 1024) { + return; + } + // done. + + if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) + { + VectorMA (org, -p->height, vup, point); + VectorMA (point, -p->width, vright, point); + VectorCopy (point, verts[0].xyz); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255 * p->alpha; + + VectorMA (org, -p->height, vup, point); + VectorMA (point, p->width, vright, point); + VectorCopy (point, verts[1].xyz); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255 * p->alpha; + + VectorMA (org, p->height, vup, point); + VectorMA (point, p->width, vright, point); + VectorCopy (point, verts[2].xyz); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255 * p->alpha; + + VectorMA (org, p->height, vup, point); + VectorMA (point, -p->width, vright, point); + VectorCopy (point, verts[3].xyz); + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255 * p->alpha; + } + else + { + VectorMA (org, -p->height, vup, point); + VectorMA (point, -p->width, vright, point); + VectorCopy( point, TRIverts[0].xyz ); + TRIverts[0].st[0] = 1; + TRIverts[0].st[1] = 0; + TRIverts[0].modulate[0] = 255; + TRIverts[0].modulate[1] = 255; + TRIverts[0].modulate[2] = 255; + TRIverts[0].modulate[3] = 255 * p->alpha; + + VectorMA (org, p->height, vup, point); + VectorMA (point, -p->width, vright, point); + VectorCopy (point, TRIverts[1].xyz); + TRIverts[1].st[0] = 0; + TRIverts[1].st[1] = 0; + TRIverts[1].modulate[0] = 255; + TRIverts[1].modulate[1] = 255; + TRIverts[1].modulate[2] = 255; + TRIverts[1].modulate[3] = 255 * p->alpha; + + VectorMA (org, p->height, vup, point); + VectorMA (point, p->width, vright, point); + VectorCopy (point, TRIverts[2].xyz); + TRIverts[2].st[0] = 0; + TRIverts[2].st[1] = 1; + TRIverts[2].modulate[0] = 255; + TRIverts[2].modulate[1] = 255; + TRIverts[2].modulate[2] = 255; + TRIverts[2].modulate[3] = 255 * p->alpha; + } + + } + else if (p->type == P_SPRITE) + { + vec3_t rr, ru; + vec3_t rotate_ang; + + VectorSet (color, 1.0, 1.0, 1.0); + time = cg.time - p->time; + time2 = p->endtime - p->time; + ratio = time / time2; + + width = p->width + ( ratio * ( p->endwidth - p->width) ); + height = p->height + ( ratio * ( p->endheight - p->height) ); + + if (p->roll) { + vectoangles( cg.refdef.viewaxis[0], rotate_ang ); + rotate_ang[ROLL] += p->roll; + AngleVectors ( rotate_ang, NULL, rr, ru); + } + + if (p->roll) { + VectorMA (org, -height, ru, point); + VectorMA (point, -width, rr, point); + } else { + VectorMA (org, -height, vup, point); + VectorMA (point, -width, vright, point); + } + VectorCopy (point, verts[0].xyz); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255; + + if (p->roll) { + VectorMA (point, 2*height, ru, point); + } else { + VectorMA (point, 2*height, vup, point); + } + VectorCopy (point, verts[1].xyz); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255; + + if (p->roll) { + VectorMA (point, 2*width, rr, point); + } else { + VectorMA (point, 2*width, vright, point); + } + VectorCopy (point, verts[2].xyz); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255; + + if (p->roll) { + VectorMA (point, -2*height, ru, point); + } else { + VectorMA (point, -2*height, vup, point); + } + VectorCopy (point, verts[3].xyz); + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255; + } + else if (p->type == P_SMOKE || p->type == P_SMOKE_IMPACT) + {// create a front rotating facing polygon + + if ( p->type == P_SMOKE_IMPACT && Distance( cg.snap->ps.origin, org ) > 1024) { + return; + } + + if (p->color == BLOODRED) + VectorSet (color, 0.22f, 0.0f, 0.0f); + else if (p->color == GREY75) + { + float len; + float greyit; + float val; + len = Distance (cg.snap->ps.origin, org); + if (!len) + len = 1; + + val = 4096/len; + greyit = 0.25 * val; + if (greyit > 0.5) + greyit = 0.5; + + VectorSet (color, greyit, greyit, greyit); + } + else + VectorSet (color, 1.0, 1.0, 1.0); + + time = cg.time - p->time; + time2 = p->endtime - p->time; + ratio = time / time2; + + if (cg.time > p->startfade) + { + invratio = 1 - ( (cg.time - p->startfade) / (p->endtime - p->startfade) ); + + if (p->color == EMISIVEFADE) + { + float fval; + fval = (invratio * invratio); + if (fval < 0) + fval = 0; + VectorSet (color, fval , fval , fval ); + } + invratio *= p->alpha; + } + else + invratio = 1 * p->alpha; + + if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) + invratio = 1; + + if (invratio > 1) + invratio = 1; + + width = p->width + ( ratio * ( p->endwidth - p->width) ); + height = p->height + ( ratio * ( p->endheight - p->height) ); + + if (p->type != P_SMOKE_IMPACT) + { + vec3_t temp; + + vectoangles (rforward, temp); + p->accumroll += p->roll; + temp[ROLL] += p->accumroll * 0.1; + AngleVectors ( temp, NULL, rright2, rup2); + } + else + { + VectorCopy (rright, rright2); + VectorCopy (rup, rup2); + } + + if (p->rotate) + { + VectorMA (org, -height, rup2, point); + VectorMA (point, -width, rright2, point); + } + else + { + VectorMA (org, -p->height, vup, point); + VectorMA (point, -p->width, vright, point); + } + VectorCopy (point, verts[0].xyz); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255 * color[0]; + verts[0].modulate[1] = 255 * color[1]; + verts[0].modulate[2] = 255 * color[2]; + verts[0].modulate[3] = 255 * invratio; + + if (p->rotate) + { + VectorMA (org, -height, rup2, point); + VectorMA (point, width, rright2, point); + } + else + { + VectorMA (org, -p->height, vup, point); + VectorMA (point, p->width, vright, point); + } + VectorCopy (point, verts[1].xyz); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255 * color[0]; + verts[1].modulate[1] = 255 * color[1]; + verts[1].modulate[2] = 255 * color[2]; + verts[1].modulate[3] = 255 * invratio; + + if (p->rotate) + { + VectorMA (org, height, rup2, point); + VectorMA (point, width, rright2, point); + } + else + { + VectorMA (org, p->height, vup, point); + VectorMA (point, p->width, vright, point); + } + VectorCopy (point, verts[2].xyz); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255 * color[0]; + verts[2].modulate[1] = 255 * color[1]; + verts[2].modulate[2] = 255 * color[2]; + verts[2].modulate[3] = 255 * invratio; + + if (p->rotate) + { + VectorMA (org, height, rup2, point); + VectorMA (point, -width, rright2, point); + } + else + { + VectorMA (org, p->height, vup, point); + VectorMA (point, -p->width, vright, point); + } + VectorCopy (point, verts[3].xyz); + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255 * color[0]; + verts[3].modulate[1] = 255 * color[1]; + verts[3].modulate[2] = 255 * color[2]; + verts[3].modulate[3] = 255 * invratio; + + } + else if (p->type == P_BLEED) + { + vec3_t rr, ru; + vec3_t rotate_ang; + float alpha; + + alpha = p->alpha; + + if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) + alpha = 1; + + if (p->roll) + { + vectoangles( cg.refdef.viewaxis[0], rotate_ang ); + rotate_ang[ROLL] += p->roll; + AngleVectors ( rotate_ang, NULL, rr, ru); + } + else + { + VectorCopy (vup, ru); + VectorCopy (vright, rr); + } + + VectorMA (org, -p->height, ru, point); + VectorMA (point, -p->width, rr, point); + VectorCopy (point, verts[0].xyz); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 111; + verts[0].modulate[1] = 19; + verts[0].modulate[2] = 9; + verts[0].modulate[3] = 255 * alpha; + + VectorMA (org, -p->height, ru, point); + VectorMA (point, p->width, rr, point); + VectorCopy (point, verts[1].xyz); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 111; + verts[1].modulate[1] = 19; + verts[1].modulate[2] = 9; + verts[1].modulate[3] = 255 * alpha; + + VectorMA (org, p->height, ru, point); + VectorMA (point, p->width, rr, point); + VectorCopy (point, verts[2].xyz); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 111; + verts[2].modulate[1] = 19; + verts[2].modulate[2] = 9; + verts[2].modulate[3] = 255 * alpha; + + VectorMA (org, p->height, ru, point); + VectorMA (point, -p->width, rr, point); + VectorCopy (point, verts[3].xyz); + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 111; + verts[3].modulate[1] = 19; + verts[3].modulate[2] = 9; + verts[3].modulate[3] = 255 * alpha; + + } + else if (p->type == P_FLAT_SCALEUP) + { + float width, height; + float sinR, cosR; + + if (p->color == BLOODRED) + VectorSet (color, 1, 1, 1); + else + VectorSet (color, 0.5, 0.5, 0.5); + + time = cg.time - p->time; + time2 = p->endtime - p->time; + ratio = time / time2; + + width = p->width + ( ratio * ( p->endwidth - p->width) ); + height = p->height + ( ratio * ( p->endheight - p->height) ); + + if (width > p->endwidth) + width = p->endwidth; + + if (height > p->endheight) + height = p->endheight; + + sinR = height * sin(DEG2RAD(p->roll)) * sqrt(2); + cosR = width * cos(DEG2RAD(p->roll)) * sqrt(2); + + VectorCopy (org, verts[0].xyz); + verts[0].xyz[0] -= sinR; + verts[0].xyz[1] -= cosR; + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255 * color[0]; + verts[0].modulate[1] = 255 * color[1]; + verts[0].modulate[2] = 255 * color[2]; + verts[0].modulate[3] = 255; + + VectorCopy (org, verts[1].xyz); + verts[1].xyz[0] -= cosR; + verts[1].xyz[1] += sinR; + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255 * color[0]; + verts[1].modulate[1] = 255 * color[1]; + verts[1].modulate[2] = 255 * color[2]; + verts[1].modulate[3] = 255; + + VectorCopy (org, verts[2].xyz); + verts[2].xyz[0] += sinR; + verts[2].xyz[1] += cosR; + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255 * color[0]; + verts[2].modulate[1] = 255 * color[1]; + verts[2].modulate[2] = 255 * color[2]; + verts[2].modulate[3] = 255; + + VectorCopy (org, verts[3].xyz); + verts[3].xyz[0] += cosR; + verts[3].xyz[1] -= sinR; + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255 * color[0]; + verts[3].modulate[1] = 255 * color[1]; + verts[3].modulate[2] = 255 * color[2]; + verts[3].modulate[3] = 255; + } + else if (p->type == P_FLAT) + { + + VectorCopy (org, verts[0].xyz); + verts[0].xyz[0] -= p->height; + verts[0].xyz[1] -= p->width; + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255; + + VectorCopy (org, verts[1].xyz); + verts[1].xyz[0] -= p->height; + verts[1].xyz[1] += p->width; + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255; + + VectorCopy (org, verts[2].xyz); + verts[2].xyz[0] += p->height; + verts[2].xyz[1] += p->width; + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255; + + VectorCopy (org, verts[3].xyz); + verts[3].xyz[0] += p->height; + verts[3].xyz[1] -= p->width; + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255; + + } + // Ridah + else if (p->type == P_ANIM) { + vec3_t rr, ru; + vec3_t rotate_ang; + int i, j; + + time = cg.time - p->time; + time2 = p->endtime - p->time; + ratio = time / time2; + if (ratio >= 1.0f) { + ratio = 0.9999f; + } + + width = p->width + ( ratio * ( p->endwidth - p->width) ); + height = p->height + ( ratio * ( p->endheight - p->height) ); + + // if we are "inside" this sprite, don't draw + if (Distance( cg.snap->ps.origin, org ) < width/1.5) { + return; + } + + i = p->shaderAnim; + j = (int)floor(ratio * shaderAnimCounts[p->shaderAnim]); + p->pshader = shaderAnims[i][j]; + + if (p->roll) { + vectoangles( cg.refdef.viewaxis[0], rotate_ang ); + rotate_ang[ROLL] += p->roll; + AngleVectors ( rotate_ang, NULL, rr, ru); + } + + if (p->roll) { + VectorMA (org, -height, ru, point); + VectorMA (point, -width, rr, point); + } else { + VectorMA (org, -height, vup, point); + VectorMA (point, -width, vright, point); + } + VectorCopy (point, verts[0].xyz); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255; + + if (p->roll) { + VectorMA (point, 2*height, ru, point); + } else { + VectorMA (point, 2*height, vup, point); + } + VectorCopy (point, verts[1].xyz); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255; + + if (p->roll) { + VectorMA (point, 2*width, rr, point); + } else { + VectorMA (point, 2*width, vright, point); + } + VectorCopy (point, verts[2].xyz); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255; + + if (p->roll) { + VectorMA (point, -2*height, ru, point); + } else { + VectorMA (point, -2*height, vup, point); + } + VectorCopy (point, verts[3].xyz); + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255; + } + // done. + + if (!p->pshader) { +// (SA) temp commented out for DM +// CG_Printf ("CG_AddParticleToScene type %d p->pshader == ZERO\n", p->type); + return; + } + + if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY) + trap_R_AddPolyToScene( p->pshader, 3, TRIverts ); + else + trap_R_AddPolyToScene( p->pshader, 4, verts ); + +} + +// Ridah, made this static so it doesn't interfere with other files +static float roll = 0.0; + +/* +=============== +CG_AddParticles +=============== +*/ +void CG_AddParticles (void) +{ + cparticle_t *p, *next; + float alpha; + float time, time2; + vec3_t org; + int color; + cparticle_t *active, *tail; + int type; + vec3_t rotate_ang; + + if (!initparticles) + CG_ClearParticles (); + + VectorCopy( cg.refdef.viewaxis[0], vforward ); + VectorCopy( cg.refdef.viewaxis[1], vright ); + VectorCopy( cg.refdef.viewaxis[2], vup ); + + vectoangles( cg.refdef.viewaxis[0], rotate_ang ); + roll += ((cg.time - oldtime) * 0.1) ; + rotate_ang[ROLL] += (roll*0.9); + AngleVectors ( rotate_ang, rforward, rright, rup); + + oldtime = cg.time; + + active = NULL; + tail = NULL; + + for (p=active_particles ; p ; p=next) + { + + next = p->next; + + time = (cg.time - p->time)*0.001; + + alpha = p->alpha + time*p->alphavel; + if (alpha <= 0) + { // faded out + p->next = free_particles; + free_particles = p; + p->type = 0; + p->color = 0; + p->alpha = 0; + continue; + } + + if (p->type == P_SMOKE || p->type == P_ANIM || p->type == P_BLEED || p->type == P_SMOKE_IMPACT) + { + if (cg.time > p->endtime) + { + p->next = free_particles; + free_particles = p; + p->type = 0; + p->color = 0; + p->alpha = 0; + + continue; + } + + } + + if (p->type == P_WEATHER_FLURRY) + { + if (cg.time > p->endtime) + { + p->next = free_particles; + free_particles = p; + p->type = 0; + p->color = 0; + p->alpha = 0; + + continue; + } + } + + + if (p->type == P_FLAT_SCALEUP_FADE) + { + if (cg.time > p->endtime) + { + p->next = free_particles; + free_particles = p; + p->type = 0; + p->color = 0; + p->alpha = 0; + continue; + } + + } + + if ((p->type == P_BAT || p->type == P_SPRITE) && p->endtime < 0) { + // temporary sprite + CG_AddParticleToScene (p, p->org, alpha); + p->next = free_particles; + free_particles = p; + p->type = 0; + p->color = 0; + p->alpha = 0; + continue; + } + + p->next = NULL; + if (!tail) + active = tail = p; + else + { + tail->next = p; + tail = p; + } + + if (alpha > 1.0) + alpha = 1; + + color = p->color; + + time2 = time*time; + + org[0] = p->org[0] + p->vel[0]*time + p->accel[0]*time2; + org[1] = p->org[1] + p->vel[1]*time + p->accel[1]*time2; + org[2] = p->org[2] + p->vel[2]*time + p->accel[2]*time2; + + type = p->type; + + CG_AddParticleToScene (p, org, alpha); + } + + active_particles = active; +} + +/* +====================== +CG_AddParticles +====================== +*/ +void CG_ParticleSnowFlurry (qhandle_t pshader, centity_t *cent) +{ + cparticle_t *p; + qboolean turb = qtrue; + + if (!pshader) + CG_Printf ("CG_ParticleSnowFlurry pshader == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->color = 0; + p->alpha = 0.90f; + p->alphavel = 0; + + p->start = cent->currentState.origin2[0]; + p->end = cent->currentState.origin2[1]; + + p->endtime = cg.time + cent->currentState.time; + p->startfade = cg.time + cent->currentState.time2; + + p->pshader = pshader; + + if (rand()%100 > 90) + { + p->height = 32; + p->width = 32; + p->alpha = 0.10f; + } + else + { + p->height = 1; + p->width = 1; + } + + p->vel[2] = -20; + + p->type = P_WEATHER_FLURRY; + + if (turb) + p->vel[2] = -10; + + VectorCopy(cent->currentState.origin, p->org); + + p->org[0] = p->org[0]; + p->org[1] = p->org[1]; + p->org[2] = p->org[2]; + + p->vel[0] = p->vel[1] = 0; + + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + p->vel[0] += cent->currentState.angles[0] * 32 + (crandom() * 16); + p->vel[1] += cent->currentState.angles[1] * 32 + (crandom() * 16); + p->vel[2] += cent->currentState.angles[2]; + + if (turb) + { + p->accel[0] = crandom () * 16; + p->accel[1] = crandom () * 16; + } + +} + +void CG_ParticleSnow (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum) +{ + cparticle_t *p; + + if (!pshader) + CG_Printf ("CG_ParticleSnow pshader == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->color = 0; + p->alpha = 0.40f; + p->alphavel = 0; + p->start = origin[2]; + p->end = origin2[2]; + p->pshader = pshader; + p->height = 1; + p->width = 1; + + p->vel[2] = -50; + + if (turb) + { + p->type = P_WEATHER_TURBULENT; + p->vel[2] = -50 * 1.3; + } + else + { + p->type = P_WEATHER; + } + + VectorCopy(origin, p->org); + + p->org[0] = p->org[0] + ( crandom() * range); + p->org[1] = p->org[1] + ( crandom() * range); + p->org[2] = p->org[2] + ( crandom() * (p->start - p->end)); + + p->vel[0] = p->vel[1] = 0; + + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + if (turb) + { + p->vel[0] = crandom() * 16; + p->vel[1] = crandom() * 16; + } + + // Rafael snow pvs check + p->snum = snum; + p->link = qtrue; + +} + +void CG_ParticleBubble (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum) +{ + cparticle_t *p; + float randsize; + + if (!pshader) + CG_Printf ("CG_ParticleSnow pshader == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->color = 0; + p->alpha = 0.40f; + p->alphavel = 0; + p->start = origin[2]; + p->end = origin2[2]; + p->pshader = pshader; + + randsize = 1 + (crandom() * 0.5); + + p->height = randsize; + p->width = randsize; + + p->vel[2] = 50 + ( crandom() * 10 ); + + if (turb) + { + p->type = P_BUBBLE_TURBULENT; + p->vel[2] = 50 * 1.3; + } + else + { + p->type = P_BUBBLE; + } + + VectorCopy(origin, p->org); + + p->org[0] = p->org[0] + ( crandom() * range); + p->org[1] = p->org[1] + ( crandom() * range); + p->org[2] = p->org[2] + ( crandom() * (p->start - p->end)); + + p->vel[0] = p->vel[1] = 0; + + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + if (turb) + { + p->vel[0] = crandom() * 4; + p->vel[1] = crandom() * 4; + } + + // Rafael snow pvs check + p->snum = snum; + p->link = qtrue; + +} + +void CG_ParticleSmoke (qhandle_t pshader, centity_t *cent) +{ + + // using cent->density = enttime + // cent->frame = startfade + cparticle_t *p; + + if (!pshader) + CG_Printf ("CG_ParticleSmoke == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + + p->endtime = cg.time + cent->currentState.time; + p->startfade = cg.time + cent->currentState.time2; + + p->color = 0; + p->alpha = 1.0; + p->alphavel = 0; + p->start = cent->currentState.origin[2]; + p->end = cent->currentState.origin2[2]; + p->pshader = pshader; + p->rotate = qfalse; + p->height = 8; + p->width = 8; + p->endheight = 32; + p->endwidth = 32; + p->type = P_SMOKE; + + VectorCopy(cent->currentState.origin, p->org); + + p->vel[0] = p->vel[1] = 0; + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + p->vel[2] = 5; + + if (cent->currentState.frame == 1)// reverse gravity + p->vel[2] *= -1; + + p->roll = 8 + (crandom() * 4); +} + + +void CG_ParticleBulletDebris (vec3_t org, vec3_t vel, int duration) +{ + + cparticle_t *p; + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + + p->endtime = cg.time + duration; + p->startfade = cg.time + duration/2; + + p->color = EMISIVEFADE; + p->alpha = 1.0; + p->alphavel = 0; + + p->height = 0.5; + p->width = 0.5; + p->endheight = 0.5; + p->endwidth = 0.5; + + p->pshader = cgs.media.tracerShader; + + p->type = P_SMOKE; + + VectorCopy(org, p->org); + + p->vel[0] = vel[0]; + p->vel[1] = vel[1]; + p->vel[2] = vel[2]; + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + p->accel[2] = -60; + p->vel[2] += -20; + +} + +/* +====================== +CG_ParticleExplosion +====================== +*/ + +void CG_ParticleExplosion (char *animStr, vec3_t origin, vec3_t vel, int duration, int sizeStart, int sizeEnd) +{ + cparticle_t *p; + int anim; + + if (animStr < (char *)10) + CG_Error( "CG_ParticleExplosion: animStr is probably an index rather than a string" ); + + // find the animation string + for (anim=0; shaderAnimNames[anim]; anim++) { + if (!Q_stricmp( animStr, shaderAnimNames[anim] )) + break; + } + if (!shaderAnimNames[anim]) { + CG_Error("CG_ParticleExplosion: unknown animation string: %s\n", animStr); + return; + } + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->alpha = 1.0; + p->alphavel = 0; + + if (duration < 0) { + duration *= -1; + p->roll = 0; + } else { + p->roll = crandom()*179; + } + + p->shaderAnim = anim; + + p->width = sizeStart; + p->height = sizeStart*shaderAnimSTRatio[anim]; // for sprites that are stretch in either direction + + p->endheight = sizeEnd; + p->endwidth = sizeEnd*shaderAnimSTRatio[anim]; + + p->endtime = cg.time + duration; + + p->type = P_ANIM; + + VectorCopy( origin, p->org ); + VectorCopy( vel, p->vel ); + VectorClear( p->accel ); + +} + +// Rafael Shrapnel +void CG_AddParticleShrapnel (localEntity_t *le) +{ + return; +} +// done. + +int CG_NewParticleArea (int num) +{ + // const char *str; + char *str; + char *token; + int type; + vec3_t origin, origin2; + int i; + float range = 0; + int turb; + int numparticles; + int snum; + + str = (char *) CG_ConfigString (num); + if (!str[0]) + return (0); + + // returns type 128 64 or 32 + token = COM_Parse (&str); + type = atoi (token); + + if (type == 1) + range = 128; + else if (type == 2) + range = 64; + else if (type == 3) + range = 32; + else if (type == 0) + range = 256; + else if (type == 4) + range = 8; + else if (type == 5) + range = 16; + else if (type == 6) + range = 32; + else if (type == 7) + range = 64; + + + for (i=0; i<3; i++) + { + token = COM_Parse (&str); + origin[i] = atof (token); + } + + for (i=0; i<3; i++) + { + token = COM_Parse (&str); + origin2[i] = atof (token); + } + + token = COM_Parse (&str); + numparticles = atoi (token); + + token = COM_Parse (&str); + turb = atoi (token); + + token = COM_Parse (&str); + snum = atoi (token); + + for (i=0; i= 4) + CG_ParticleBubble (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum); + else + CG_ParticleSnow (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum); + } + + return (1); +} + +void CG_SnowLink (centity_t *cent, qboolean particleOn) +{ + cparticle_t *p, *next; + int id; + + id = cent->currentState.frame; + + for (p=active_particles ; p ; p=next) + { + next = p->next; + + if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT) + { + if (p->snum == id) + { + if (particleOn) + p->link = qtrue; + else + p->link = qfalse; + } + } + + } +} + +void CG_ParticleImpactSmokePuff (qhandle_t pshader, vec3_t origin) +{ + cparticle_t *p; + + if (!pshader) + CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->alpha = 0.25; + p->alphavel = 0; + p->roll = crandom()*179; + + p->pshader = pshader; + + p->endtime = cg.time + 1000; + p->startfade = cg.time + 100; + + p->width = rand()%4 + 8; + p->height = rand()%4 + 8; + + p->endheight = p->height *2; + p->endwidth = p->width * 2; + + p->endtime = cg.time + 500; + + p->type = P_SMOKE_IMPACT; + + VectorCopy( origin, p->org ); + VectorSet(p->vel, 0, 0, 20); + VectorSet(p->accel, 0, 0, 20); + + p->rotate = qtrue; +} + +void CG_Particle_Bleed (qhandle_t pshader, vec3_t start, vec3_t dir, int fleshEntityNum, int duration) +{ + cparticle_t *p; + + if (!pshader) + CG_Printf ("CG_Particle_Bleed pshader == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->alpha = 1.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = pshader; + + p->endtime = cg.time + duration; + + if (fleshEntityNum) + p->startfade = cg.time; + else + p->startfade = cg.time + 100; + + p->width = 4; + p->height = 4; + + p->endheight = 4+rand()%3; + p->endwidth = p->endheight; + + p->type = P_SMOKE; + + VectorCopy( start, p->org ); + p->vel[0] = 0; + p->vel[1] = 0; + p->vel[2] = -20; + VectorClear( p->accel ); + + p->rotate = qfalse; + + p->roll = rand()%179; + + p->color = BLOODRED; + p->alpha = 0.75; + +} + +void CG_Particle_OilParticle (qhandle_t pshader, centity_t *cent) +{ + cparticle_t *p; + + int time; + int time2; + float ratio; + + float duration = 1500; + + time = cg.time; + time2 = cg.time + cent->currentState.time; + + ratio =(float)1 - ((float)time / (float)time2); + + if (!pshader) + CG_Printf ("CG_Particle_OilParticle == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->alpha = 1.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = pshader; + + p->endtime = cg.time + duration; + + p->startfade = p->endtime; + + p->width = 1; + p->height = 3; + + p->endheight = 3; + p->endwidth = 1; + + p->type = P_SMOKE; + + VectorCopy(cent->currentState.origin, p->org ); + + p->vel[0] = (cent->currentState.origin2[0] * (16 * ratio)); + p->vel[1] = (cent->currentState.origin2[1] * (16 * ratio)); + p->vel[2] = (cent->currentState.origin2[2]); + + p->snum = 1.0f; + + VectorClear( p->accel ); + + p->accel[2] = -20; + + p->rotate = qfalse; + + p->roll = rand()%179; + + p->alpha = 0.75; + +} + + +void CG_Particle_OilSlick (qhandle_t pshader, centity_t *cent) +{ + cparticle_t *p; + + if (!pshader) + CG_Printf ("CG_Particle_OilSlick == ZERO!\n"); + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + + if (cent->currentState.angles2[2]) + p->endtime = cg.time + cent->currentState.angles2[2]; + else + p->endtime = cg.time + 60000; + + p->startfade = p->endtime; + + p->alpha = 1.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = pshader; + + if (cent->currentState.angles2[0] || cent->currentState.angles2[1]) + { + p->width = cent->currentState.angles2[0]; + p->height = cent->currentState.angles2[0]; + + p->endheight = cent->currentState.angles2[1]; + p->endwidth = cent->currentState.angles2[1]; + } + else + { + p->width = 8; + p->height = 8; + + p->endheight = 16; + p->endwidth = 16; + } + + p->type = P_FLAT_SCALEUP; + + p->snum = 1.0; + + VectorCopy(cent->currentState.origin, p->org ); + + p->org[2]+= 0.55 + (crandom() * 0.5); + + p->vel[0] = 0; + p->vel[1] = 0; + p->vel[2] = 0; + VectorClear( p->accel ); + + p->rotate = qfalse; + + p->roll = rand()%179; + + p->alpha = 0.75; + +} + +void CG_OilSlickRemove (centity_t *cent) +{ + cparticle_t *p, *next; + int id; + + id = 1.0f; + + if (!id) + CG_Printf ("CG_OilSlickRevove NULL id\n"); + + for (p=active_particles ; p ; p=next) + { + next = p->next; + + if (p->type == P_FLAT_SCALEUP) + { + if (p->snum == id) + { + p->endtime = cg.time + 100; + p->startfade = p->endtime; + p->type = P_FLAT_SCALEUP_FADE; + + } + } + + } +} + +qboolean ValidBloodPool (vec3_t start) +{ +#define EXTRUDE_DIST 0.5 + + vec3_t angles; + vec3_t right, up; + vec3_t this_pos, x_pos, center_pos, end_pos; + float x, y; + float fwidth, fheight; + trace_t trace; + vec3_t normal; + + fwidth = 16; + fheight = 16; + + VectorSet (normal, 0, 0, 1); + + vectoangles (normal, angles); + AngleVectors (angles, NULL, right, up); + + VectorMA (start, EXTRUDE_DIST, normal, center_pos); + + for (x= -fwidth/2; xendpos, start); + legit = ValidBloodPool (start); + + if (!legit) + return; + + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + + p->endtime = cg.time + 3000; + p->startfade = p->endtime; + + p->alpha = 1.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = pshader; + + rndSize = 0.4 + random()*0.6; + + p->width = 8*rndSize; + p->height = 8*rndSize; + + p->endheight = 16*rndSize; + p->endwidth = 16*rndSize; + + p->type = P_FLAT_SCALEUP; + + VectorCopy(start, p->org ); + + p->vel[0] = 0; + p->vel[1] = 0; + p->vel[2] = 0; + VectorClear( p->accel ); + + p->rotate = qfalse; + + p->roll = rand()%179; + + p->alpha = 0.75; + + p->color = BLOODRED; +} + +#define NORMALSIZE 16 +#define LARGESIZE 32 + +void CG_ParticleBloodCloud (centity_t *cent, vec3_t origin, vec3_t dir) +{ + float length; + float dist; + float crittersize; + vec3_t angles, forward; + vec3_t point; + cparticle_t *p; + int i; + + dist = 0; + + length = VectorLength (dir); + vectoangles (dir, angles); + AngleVectors (angles, forward, NULL, NULL); + + crittersize = LARGESIZE; + + if (length) + dist = length / crittersize; + + if (dist < 1) + dist = 1; + + VectorCopy (origin, point); + + for (i=0; inext; + p->next = active_particles; + active_particles = p; + + p->time = cg.time; + p->alpha = 1.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = cgs.media.smokePuffShader; + + p->endtime = cg.time + 350 + (crandom() * 100); + + p->startfade = cg.time; + + p->width = LARGESIZE; + p->height = LARGESIZE; + p->endheight = LARGESIZE; + p->endwidth = LARGESIZE; + + p->type = P_SMOKE; + + VectorCopy( origin, p->org ); + + p->vel[0] = 0; + p->vel[1] = 0; + p->vel[2] = -1; + + VectorClear( p->accel ); + + p->rotate = qfalse; + + p->roll = rand()%179; + + p->color = BLOODRED; + + p->alpha = 0.75; + + } + + +} + +void CG_ParticleSparks (vec3_t org, vec3_t vel, int duration, float x, float y, float speed) +{ + cparticle_t *p; + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + + p->endtime = cg.time + duration; + p->startfade = cg.time + duration/2; + + p->color = EMISIVEFADE; + p->alpha = 0.4f; + p->alphavel = 0; + + p->height = 0.5; + p->width = 0.5; + p->endheight = 0.5; + p->endwidth = 0.5; + + p->pshader = cgs.media.tracerShader; + + p->type = P_SMOKE; + + VectorCopy(org, p->org); + + p->org[0] += (crandom() * x); + p->org[1] += (crandom() * y); + + p->vel[0] = vel[0]; + p->vel[1] = vel[1]; + p->vel[2] = vel[2]; + + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + p->vel[0] += (crandom() * 4); + p->vel[1] += (crandom() * 4); + p->vel[2] += (20 + (crandom() * 10)) * speed; + + p->accel[0] = crandom () * 4; + p->accel[1] = crandom () * 4; + +} + +void CG_ParticleDust (centity_t *cent, vec3_t origin, vec3_t dir) +{ + float length; + float dist; + float crittersize; + vec3_t angles, forward; + vec3_t point; + cparticle_t *p; + int i; + + dist = 0; + + VectorNegate (dir, dir); + length = VectorLength (dir); + vectoangles (dir, angles); + AngleVectors (angles, forward, NULL, NULL); + + crittersize = LARGESIZE; + + if (length) + dist = length / crittersize; + + if (dist < 1) + dist = 1; + + VectorCopy (origin, point); + + for (i=0; inext; + p->next = active_particles; + active_particles = p; + + p->time = cg.time; + p->alpha = 5.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = cgs.media.smokePuffShader; + + // RF, stay around for long enough to expand and dissipate naturally + if (length) + p->endtime = cg.time + 4500 + (crandom() * 3500); + else + p->endtime = cg.time + 750 + (crandom() * 500); + + p->startfade = cg.time; + + p->width = LARGESIZE; + p->height = LARGESIZE; + + // RF, expand while falling + p->endheight = LARGESIZE*3.0; + p->endwidth = LARGESIZE*3.0; + + if (!length) + { + p->width *= 0.2f; + p->height *= 0.2f; + + p->endheight = NORMALSIZE; + p->endwidth = NORMALSIZE; + } + + p->type = P_SMOKE; + + VectorCopy( point, p->org ); + + p->vel[0] = crandom()*6; + p->vel[1] = crandom()*6; + p->vel[2] = random()*20; + + // RF, add some gravity/randomness + p->accel[0] = crandom()*3; + p->accel[1] = crandom()*3; + p->accel[2] = -PARTICLE_GRAVITY*0.4; + + VectorClear( p->accel ); + + p->rotate = qfalse; + + p->roll = rand()%179; + + p->alpha = 0.75; + + } + + +} + +void CG_ParticleMisc (qhandle_t pshader, vec3_t origin, int size, int duration, float alpha) +{ + cparticle_t *p; + + if (!pshader) + CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n"); + + if (!free_particles) + return; + + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->alpha = 1.0; + p->alphavel = 0; + p->roll = rand()%179; + + p->pshader = pshader; + + if (duration > 0) + p->endtime = cg.time + duration; + else + p->endtime = duration; + + p->startfade = cg.time; + + p->width = size; + p->height = size; + + p->endheight = size; + p->endwidth = size; + + p->type = P_SPRITE; + + VectorCopy( origin, p->org ); + + p->rotate = qfalse; +} diff --git a/reaction/engine/code/cgame/cg_players.c b/reaction/engine/code/cgame/cg_players.c new file mode 100644 index 00000000..61e26c72 --- /dev/null +++ b/reaction/engine/code/cgame/cg_players.c @@ -0,0 +1,2618 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_players.c -- handle the media and animation for player entities +#include "cg_local.h" + +char *cg_customSoundNames[MAX_CUSTOM_SOUNDS] = { + "*death1.wav", + "*death2.wav", + "*death3.wav", + "*jump1.wav", + "*pain25_1.wav", + "*pain50_1.wav", + "*pain75_1.wav", + "*pain100_1.wav", + "*falling1.wav", + "*gasp.wav", + "*drown.wav", + "*fall1.wav", + "*taunt.wav" +}; + + +/* +================ +CG_CustomSound + +================ +*/ +sfxHandle_t CG_CustomSound( int clientNum, const char *soundName ) { + clientInfo_t *ci; + int i; + + if ( soundName[0] != '*' ) { + return trap_S_RegisterSound( soundName, qfalse ); + } + + if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { + clientNum = 0; + } + ci = &cgs.clientinfo[ clientNum ]; + + for ( i = 0 ; i < MAX_CUSTOM_SOUNDS && cg_customSoundNames[i] ; i++ ) { + if ( !strcmp( soundName, cg_customSoundNames[i] ) ) { + return ci->sounds[i]; + } + } + + CG_Error( "Unknown custom sound: %s", soundName ); + return 0; +} + + + +/* +============================================================================= + +CLIENT INFO + +============================================================================= +*/ + +/* +====================== +CG_ParseAnimationFile + +Read a configuration file containing animation coutns and rates +models/players/visor/animation.cfg, etc +====================== +*/ +static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci ) { + char *text_p, *prev; + int len; + int i; + char *token; + float fps; + int skip; + char text[20000]; + fileHandle_t f; + animation_t *animations; + + animations = ci->animations; + + // load the file + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + if ( len <= 0 ) { + return qfalse; + } + if ( len >= sizeof( text ) - 1 ) { + CG_Printf( "File %s too long\n", filename ); + trap_FS_FCloseFile( f ); + return qfalse; + } + trap_FS_Read( text, len, f ); + text[len] = 0; + trap_FS_FCloseFile( f ); + + // parse the text + text_p = text; + skip = 0; // quite the compiler warning + + ci->footsteps = FOOTSTEP_NORMAL; + VectorClear( ci->headOffset ); + ci->gender = GENDER_MALE; + ci->fixedlegs = qfalse; + ci->fixedtorso = qfalse; + + // read optional parameters + while ( 1 ) { + prev = text_p; // so we can unget + token = COM_Parse( &text_p ); + if ( !token ) { + break; + } + if ( !Q_stricmp( token, "footsteps" ) ) { + token = COM_Parse( &text_p ); + if ( !token ) { + break; + } + if ( !Q_stricmp( token, "default" ) || !Q_stricmp( token, "normal" ) ) { + ci->footsteps = FOOTSTEP_NORMAL; + } else if ( !Q_stricmp( token, "boot" ) ) { + ci->footsteps = FOOTSTEP_BOOT; + } else if ( !Q_stricmp( token, "flesh" ) ) { + ci->footsteps = FOOTSTEP_FLESH; + } else if ( !Q_stricmp( token, "mech" ) ) { + ci->footsteps = FOOTSTEP_MECH; + } else if ( !Q_stricmp( token, "energy" ) ) { + ci->footsteps = FOOTSTEP_ENERGY; + } else { + CG_Printf( "Bad footsteps parm in %s: %s\n", filename, token ); + } + continue; + } else if ( !Q_stricmp( token, "headoffset" ) ) { + for ( i = 0 ; i < 3 ; i++ ) { + token = COM_Parse( &text_p ); + if ( !token ) { + break; + } + ci->headOffset[i] = atof( token ); + } + continue; + } else if ( !Q_stricmp( token, "sex" ) ) { + token = COM_Parse( &text_p ); + if ( !token ) { + break; + } + if ( token[0] == 'f' || token[0] == 'F' ) { + ci->gender = GENDER_FEMALE; + } else if ( token[0] == 'n' || token[0] == 'N' ) { + ci->gender = GENDER_NEUTER; + } else { + ci->gender = GENDER_MALE; + } + continue; + } else if ( !Q_stricmp( token, "fixedlegs" ) ) { + ci->fixedlegs = qtrue; + continue; + } else if ( !Q_stricmp( token, "fixedtorso" ) ) { + ci->fixedtorso = qtrue; + continue; + } + + // if it is a number, start parsing animations + if ( token[0] >= '0' && token[0] <= '9' ) { + text_p = prev; // unget the token + break; + } + Com_Printf( "unknown token '%s' is %s\n", token, filename ); + } + + // read information for each frame + for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) { + + token = COM_Parse( &text_p ); + if ( !*token ) { + if( i >= TORSO_GETFLAG && i <= TORSO_NEGATIVE ) { + animations[i].firstFrame = animations[TORSO_GESTURE].firstFrame; + animations[i].frameLerp = animations[TORSO_GESTURE].frameLerp; + animations[i].initialLerp = animations[TORSO_GESTURE].initialLerp; + animations[i].loopFrames = animations[TORSO_GESTURE].loopFrames; + animations[i].numFrames = animations[TORSO_GESTURE].numFrames; + animations[i].reversed = qfalse; + animations[i].flipflop = qfalse; + continue; + } + break; + } + animations[i].firstFrame = atoi( token ); + // leg only frames are adjusted to not count the upper body only frames + if ( i == LEGS_WALKCR ) { + skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame; + } + if ( i >= LEGS_WALKCR && i0) { + return qtrue; + } + return qfalse; +} + +/* +========================== +CG_FindClientModelFile +========================== +*/ +static qboolean CG_FindClientModelFile( char *filename, int length, clientInfo_t *ci, const char *teamName, const char *modelName, const char *skinName, const char *base, const char *ext ) { + char *team, *charactersFolder; + int i; + + if ( cgs.gametype >= GT_TEAM ) { + switch ( ci->team ) { + case TEAM_BLUE: { + team = "blue"; + break; + } + default: { + team = "red"; + break; + } + } + } + else { + team = "default"; + } + charactersFolder = ""; + while(1) { + for ( i = 0; i < 2; i++ ) { + if ( i == 0 && teamName && *teamName ) { + // "models/players/characters/james/stroggs/lower_lily_red.skin" + Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s_%s.%s", charactersFolder, modelName, teamName, base, skinName, team, ext ); + } + else { + // "models/players/characters/james/lower_lily_red.skin" + Com_sprintf( filename, length, "models/players/%s%s/%s_%s_%s.%s", charactersFolder, modelName, base, skinName, team, ext ); + } + if ( CG_FileExists( filename ) ) { + return qtrue; + } + if ( cgs.gametype >= GT_TEAM ) { + if ( i == 0 && teamName && *teamName ) { + // "models/players/characters/james/stroggs/lower_red.skin" + Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", charactersFolder, modelName, teamName, base, team, ext ); + } + else { + // "models/players/characters/james/lower_red.skin" + Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", charactersFolder, modelName, base, team, ext ); + } + } + else { + if ( i == 0 && teamName && *teamName ) { + // "models/players/characters/james/stroggs/lower_lily.skin" + Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", charactersFolder, modelName, teamName, base, skinName, ext ); + } + else { + // "models/players/characters/james/lower_lily.skin" + Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", charactersFolder, modelName, base, skinName, ext ); + } + } + if ( CG_FileExists( filename ) ) { + return qtrue; + } + if ( !teamName || !*teamName ) { + break; + } + } + // if tried the heads folder first + if ( charactersFolder[0] ) { + break; + } + charactersFolder = "characters/"; + } + + return qfalse; +} + +/* +========================== +CG_FindClientHeadFile +========================== +*/ +static qboolean CG_FindClientHeadFile( char *filename, int length, clientInfo_t *ci, const char *teamName, const char *headModelName, const char *headSkinName, const char *base, const char *ext ) { + char *team, *headsFolder; + int i; + + if ( cgs.gametype >= GT_TEAM ) { + switch ( ci->team ) { + case TEAM_BLUE: { + team = "blue"; + break; + } + default: { + team = "red"; + break; + } + } + } + else { + team = "default"; + } + + if ( headModelName[0] == '*' ) { + headsFolder = "heads/"; + headModelName++; + } + else { + headsFolder = ""; + } + while(1) { + for ( i = 0; i < 2; i++ ) { + if ( i == 0 && teamName && *teamName ) { + Com_sprintf( filename, length, "models/players/%s%s/%s/%s%s_%s.%s", headsFolder, headModelName, headSkinName, teamName, base, team, ext ); + } + else { + Com_sprintf( filename, length, "models/players/%s%s/%s/%s_%s.%s", headsFolder, headModelName, headSkinName, base, team, ext ); + } + if ( CG_FileExists( filename ) ) { + return qtrue; + } + if ( cgs.gametype >= GT_TEAM ) { + if ( i == 0 && teamName && *teamName ) { + Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", headsFolder, headModelName, teamName, base, team, ext ); + } + else { + Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", headsFolder, headModelName, base, team, ext ); + } + } + else { + if ( i == 0 && teamName && *teamName ) { + Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", headsFolder, headModelName, teamName, base, headSkinName, ext ); + } + else { + Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", headsFolder, headModelName, base, headSkinName, ext ); + } + } + if ( CG_FileExists( filename ) ) { + return qtrue; + } + if ( !teamName || !*teamName ) { + break; + } + } + // if tried the heads folder first + if ( headsFolder[0] ) { + break; + } + headsFolder = "heads/"; + } + + return qfalse; +} + +/* +========================== +CG_RegisterClientSkin +========================== +*/ +static qboolean CG_RegisterClientSkin( clientInfo_t *ci, const char *teamName, const char *modelName, const char *skinName, const char *headModelName, const char *headSkinName ) { + char filename[MAX_QPATH]; + + /* + Com_sprintf( filename, sizeof( filename ), "models/players/%s/%slower_%s.skin", modelName, teamName, skinName ); + ci->legsSkin = trap_R_RegisterSkin( filename ); + if (!ci->legsSkin) { + Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%slower_%s.skin", modelName, teamName, skinName ); + ci->legsSkin = trap_R_RegisterSkin( filename ); + if (!ci->legsSkin) { + Com_Printf( "Leg skin load failure: %s\n", filename ); + } + } + + + Com_sprintf( filename, sizeof( filename ), "models/players/%s/%supper_%s.skin", modelName, teamName, skinName ); + ci->torsoSkin = trap_R_RegisterSkin( filename ); + if (!ci->torsoSkin) { + Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%supper_%s.skin", modelName, teamName, skinName ); + ci->torsoSkin = trap_R_RegisterSkin( filename ); + if (!ci->torsoSkin) { + Com_Printf( "Torso skin load failure: %s\n", filename ); + } + } + */ + if ( CG_FindClientModelFile( filename, sizeof(filename), ci, teamName, modelName, skinName, "lower", "skin" ) ) { + ci->legsSkin = trap_R_RegisterSkin( filename ); + } + if (!ci->legsSkin) { + Com_Printf( "Leg skin load failure: %s\n", filename ); + } + + if ( CG_FindClientModelFile( filename, sizeof(filename), ci, teamName, modelName, skinName, "upper", "skin" ) ) { + ci->torsoSkin = trap_R_RegisterSkin( filename ); + } + if (!ci->torsoSkin) { + Com_Printf( "Torso skin load failure: %s\n", filename ); + } + + if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headModelName, headSkinName, "head", "skin" ) ) { + ci->headSkin = trap_R_RegisterSkin( filename ); + } + if (!ci->headSkin) { + Com_Printf( "Head skin load failure: %s\n", filename ); + } + + // if any skins failed to load + if ( !ci->legsSkin || !ci->torsoSkin || !ci->headSkin ) { + return qfalse; + } + return qtrue; +} + +/* +========================== +CG_RegisterClientModelname +========================== +*/ +static qboolean CG_RegisterClientModelname( clientInfo_t *ci, const char *modelName, const char *skinName, const char *headModelName, const char *headSkinName, const char *teamName ) { + char filename[MAX_QPATH*2]; + const char *headName; + char newTeamName[MAX_QPATH*2]; + + if ( headModelName[0] == '\0' ) { + headName = modelName; + } + else { + headName = headModelName; + } + Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower.md3", modelName ); + ci->legsModel = trap_R_RegisterModel( filename ); + if ( !ci->legsModel ) { + Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/lower.md3", modelName ); + ci->legsModel = trap_R_RegisterModel( filename ); + if ( !ci->legsModel ) { + Com_Printf( "Failed to load model file %s\n", filename ); + return qfalse; + } + } + + Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper.md3", modelName ); + ci->torsoModel = trap_R_RegisterModel( filename ); + if ( !ci->torsoModel ) { + Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/upper.md3", modelName ); + ci->torsoModel = trap_R_RegisterModel( filename ); + if ( !ci->torsoModel ) { + Com_Printf( "Failed to load model file %s\n", filename ); + return qfalse; + } + } + + if( headName[0] == '*' ) { + Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", &headModelName[1], &headModelName[1] ); + } + else { + Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", headName ); + } + ci->headModel = trap_R_RegisterModel( filename ); + // if the head model could not be found and we didn't load from the heads folder try to load from there + if ( !ci->headModel && headName[0] != '*' ) { + Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", headModelName, headModelName ); + ci->headModel = trap_R_RegisterModel( filename ); + } + if ( !ci->headModel ) { + Com_Printf( "Failed to load model file %s\n", filename ); + return qfalse; + } + + // if any skins failed to load, return failure + if ( !CG_RegisterClientSkin( ci, teamName, modelName, skinName, headName, headSkinName ) ) { + if ( teamName && *teamName) { + Com_Printf( "Failed to load skin file: %s : %s : %s, %s : %s\n", teamName, modelName, skinName, headName, headSkinName ); + if( ci->team == TEAM_BLUE ) { + Com_sprintf(newTeamName, sizeof(newTeamName), "%s/", DEFAULT_BLUETEAM_NAME); + } + else { + Com_sprintf(newTeamName, sizeof(newTeamName), "%s/", DEFAULT_REDTEAM_NAME); + } + if ( !CG_RegisterClientSkin( ci, newTeamName, modelName, skinName, headName, headSkinName ) ) { + Com_Printf( "Failed to load skin file: %s : %s : %s, %s : %s\n", newTeamName, modelName, skinName, headName, headSkinName ); + return qfalse; + } + } else { + Com_Printf( "Failed to load skin file: %s : %s, %s : %s\n", modelName, skinName, headName, headSkinName ); + return qfalse; + } + } + + // load the animations + Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", modelName ); + if ( !CG_ParseAnimationFile( filename, ci ) ) { + Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/animation.cfg", modelName ); + if ( !CG_ParseAnimationFile( filename, ci ) ) { + Com_Printf( "Failed to load animation file %s\n", filename ); + return qfalse; + } + } + + if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headName, headSkinName, "icon", "skin" ) ) { + ci->modelIcon = trap_R_RegisterShaderNoMip( filename ); + } + else if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headName, headSkinName, "icon", "tga" ) ) { + ci->modelIcon = trap_R_RegisterShaderNoMip( filename ); + } + + if ( !ci->modelIcon ) { + return qfalse; + } + + return qtrue; +} + +/* +==================== +CG_ColorFromString +==================== +*/ +static void CG_ColorFromString( const char *v, vec3_t color ) { + int val; + + VectorClear( color ); + + val = atoi( v ); + + if ( val < 1 || val > 7 ) { + VectorSet( color, 1, 1, 1 ); + return; + } + + if ( val & 1 ) { + color[2] = 1.0f; + } + if ( val & 2 ) { + color[1] = 1.0f; + } + if ( val & 4 ) { + color[0] = 1.0f; + } +} + +/* +=================== +CG_LoadClientInfo + +Load it now, taking the disk hits. +This will usually be deferred to a safe time +=================== +*/ +static void CG_LoadClientInfo( int clientNum, clientInfo_t *ci ) { + const char *dir, *fallback; + int i, modelloaded; + const char *s; + char teamname[MAX_QPATH]; + + teamname[0] = 0; +#ifdef MISSIONPACK + if( cgs.gametype >= GT_TEAM) { + if( ci->team == TEAM_BLUE ) { + Q_strncpyz(teamname, cg_blueTeamName.string, sizeof(teamname) ); + } else { + Q_strncpyz(teamname, cg_redTeamName.string, sizeof(teamname) ); + } + } + if( teamname[0] ) { + strcat( teamname, "/" ); + } +#endif + modelloaded = qtrue; + if ( !CG_RegisterClientModelname( ci, ci->modelName, ci->skinName, ci->headModelName, ci->headSkinName, teamname ) ) { + if ( cg_buildScript.integer ) { + CG_Error( "CG_RegisterClientModelname( %s, %s, %s, %s %s ) failed", ci->modelName, ci->skinName, ci->headModelName, ci->headSkinName, teamname ); + } + + // fall back to default team name + if( cgs.gametype >= GT_TEAM) { + // keep skin name + if( ci->team == TEAM_BLUE ) { + Q_strncpyz(teamname, DEFAULT_BLUETEAM_NAME, sizeof(teamname) ); + } else { + Q_strncpyz(teamname, DEFAULT_REDTEAM_NAME, sizeof(teamname) ); + } + if ( !CG_RegisterClientModelname( ci, DEFAULT_TEAM_MODEL, ci->skinName, DEFAULT_TEAM_HEAD, ci->skinName, teamname ) ) { + CG_Error( "DEFAULT_TEAM_MODEL / skin (%s/%s) failed to register", DEFAULT_TEAM_MODEL, ci->skinName ); + } + } else { + if ( !CG_RegisterClientModelname( ci, DEFAULT_MODEL, "default", DEFAULT_MODEL, "default", teamname ) ) { + CG_Error( "DEFAULT_MODEL (%s) failed to register", DEFAULT_MODEL ); + } + } + modelloaded = qfalse; + } + + ci->newAnims = qfalse; + if ( ci->torsoModel ) { + orientation_t tag; + // if the torso model has the "tag_flag" + if ( trap_R_LerpTag( &tag, ci->torsoModel, 0, 0, 1, "tag_flag" ) ) { + ci->newAnims = qtrue; + } + } + + // sounds + dir = ci->modelName; + fallback = (cgs.gametype >= GT_TEAM) ? DEFAULT_TEAM_MODEL : DEFAULT_MODEL; + + for ( i = 0 ; i < MAX_CUSTOM_SOUNDS ; i++ ) { + s = cg_customSoundNames[i]; + if ( !s ) { + break; + } + ci->sounds[i] = 0; + // if the model didn't load use the sounds of the default model + if (modelloaded) { + ci->sounds[i] = trap_S_RegisterSound( va("sound/player/%s/%s", dir, s + 1), qfalse ); + } + if ( !ci->sounds[i] ) { + ci->sounds[i] = trap_S_RegisterSound( va("sound/player/%s/%s", fallback, s + 1), qfalse ); + } + } + + ci->deferred = qfalse; + + // reset any existing players and bodies, because they might be in bad + // frames for this new model + for ( i = 0 ; i < MAX_GENTITIES ; i++ ) { + if ( cg_entities[i].currentState.clientNum == clientNum + && cg_entities[i].currentState.eType == ET_PLAYER ) { + CG_ResetPlayerEntity( &cg_entities[i] ); + } + } +} + +/* +====================== +CG_CopyClientInfoModel +====================== +*/ +static void CG_CopyClientInfoModel( clientInfo_t *from, clientInfo_t *to ) { + VectorCopy( from->headOffset, to->headOffset ); + to->footsteps = from->footsteps; + to->gender = from->gender; + + to->legsModel = from->legsModel; + to->legsSkin = from->legsSkin; + to->torsoModel = from->torsoModel; + to->torsoSkin = from->torsoSkin; + to->headModel = from->headModel; + to->headSkin = from->headSkin; + to->modelIcon = from->modelIcon; + + to->newAnims = from->newAnims; + + memcpy( to->animations, from->animations, sizeof( to->animations ) ); + memcpy( to->sounds, from->sounds, sizeof( to->sounds ) ); +} + +/* +====================== +CG_ScanForExistingClientInfo +====================== +*/ +static qboolean CG_ScanForExistingClientInfo( clientInfo_t *ci ) { + int i; + clientInfo_t *match; + + for ( i = 0 ; i < cgs.maxclients ; i++ ) { + match = &cgs.clientinfo[ i ]; + if ( !match->infoValid ) { + continue; + } + if ( match->deferred ) { + continue; + } + if ( !Q_stricmp( ci->modelName, match->modelName ) + && !Q_stricmp( ci->skinName, match->skinName ) + && !Q_stricmp( ci->headModelName, match->headModelName ) + && !Q_stricmp( ci->headSkinName, match->headSkinName ) + && !Q_stricmp( ci->blueTeam, match->blueTeam ) + && !Q_stricmp( ci->redTeam, match->redTeam ) + && (cgs.gametype < GT_TEAM || ci->team == match->team) ) { + // this clientinfo is identical, so use it's handles + + ci->deferred = qfalse; + + CG_CopyClientInfoModel( match, ci ); + + return qtrue; + } + } + + // nothing matches, so defer the load + return qfalse; +} + +/* +====================== +CG_SetDeferredClientInfo + +We aren't going to load it now, so grab some other +client's info to use until we have some spare time. +====================== +*/ +static void CG_SetDeferredClientInfo( int clientNum, clientInfo_t *ci ) { + int i; + clientInfo_t *match; + + // if someone else is already the same models and skins we + // can just load the client info + for ( i = 0 ; i < cgs.maxclients ; i++ ) { + match = &cgs.clientinfo[ i ]; + if ( !match->infoValid || match->deferred ) { + continue; + } + if ( Q_stricmp( ci->skinName, match->skinName ) || + Q_stricmp( ci->modelName, match->modelName ) || +// Q_stricmp( ci->headModelName, match->headModelName ) || +// Q_stricmp( ci->headSkinName, match->headSkinName ) || + (cgs.gametype >= GT_TEAM && ci->team != match->team) ) { + continue; + } + // just load the real info cause it uses the same models and skins + CG_LoadClientInfo( clientNum, ci ); + return; + } + + // if we are in teamplay, only grab a model if the skin is correct + if ( cgs.gametype >= GT_TEAM ) { + for ( i = 0 ; i < cgs.maxclients ; i++ ) { + match = &cgs.clientinfo[ i ]; + if ( !match->infoValid || match->deferred ) { + continue; + } + if ( Q_stricmp( ci->skinName, match->skinName ) || + (cgs.gametype >= GT_TEAM && ci->team != match->team) ) { + continue; + } + ci->deferred = qtrue; + CG_CopyClientInfoModel( match, ci ); + return; + } + // load the full model, because we don't ever want to show + // an improper team skin. This will cause a hitch for the first + // player, when the second enters. Combat shouldn't be going on + // yet, so it shouldn't matter + CG_LoadClientInfo( clientNum, ci ); + return; + } + + // find the first valid clientinfo and grab its stuff + for ( i = 0 ; i < cgs.maxclients ; i++ ) { + match = &cgs.clientinfo[ i ]; + if ( !match->infoValid ) { + continue; + } + + ci->deferred = qtrue; + CG_CopyClientInfoModel( match, ci ); + return; + } + + // we should never get here... + CG_Printf( "CG_SetDeferredClientInfo: no valid clients!\n" ); + + CG_LoadClientInfo( clientNum, ci ); +} + + +/* +====================== +CG_NewClientInfo +====================== +*/ +void CG_NewClientInfo( int clientNum ) { + clientInfo_t *ci; + clientInfo_t newInfo; + const char *configstring; + const char *v; + char *slash; + + ci = &cgs.clientinfo[clientNum]; + + configstring = CG_ConfigString( clientNum + CS_PLAYERS ); + if ( !configstring[0] ) { + memset( ci, 0, sizeof( *ci ) ); + return; // player just left + } + + // build into a temp buffer so the defer checks can use + // the old value + memset( &newInfo, 0, sizeof( newInfo ) ); + + // isolate the player's name + v = Info_ValueForKey(configstring, "n"); + Q_strncpyz( newInfo.name, v, sizeof( newInfo.name ) ); + + // colors + v = Info_ValueForKey( configstring, "c1" ); + CG_ColorFromString( v, newInfo.color1 ); + + v = Info_ValueForKey( configstring, "c2" ); + CG_ColorFromString( v, newInfo.color2 ); + + // bot skill + v = Info_ValueForKey( configstring, "skill" ); + newInfo.botSkill = atoi( v ); + + // handicap + v = Info_ValueForKey( configstring, "hc" ); + newInfo.handicap = atoi( v ); + + // wins + v = Info_ValueForKey( configstring, "w" ); + newInfo.wins = atoi( v ); + + // losses + v = Info_ValueForKey( configstring, "l" ); + newInfo.losses = atoi( v ); + + // team + v = Info_ValueForKey( configstring, "t" ); + newInfo.team = atoi( v ); + + // team task + v = Info_ValueForKey( configstring, "tt" ); + newInfo.teamTask = atoi(v); + + // team leader + v = Info_ValueForKey( configstring, "tl" ); + newInfo.teamLeader = atoi(v); + + v = Info_ValueForKey( configstring, "g_redteam" ); + Q_strncpyz(newInfo.redTeam, v, MAX_TEAMNAME); + + v = Info_ValueForKey( configstring, "g_blueteam" ); + Q_strncpyz(newInfo.blueTeam, v, MAX_TEAMNAME); + + // model + v = Info_ValueForKey( configstring, "model" ); + if ( cg_forceModel.integer ) { + // forcemodel makes everyone use a single model + // to prevent load hitches + char modelStr[MAX_QPATH]; + char *skin; + + if( cgs.gametype >= GT_TEAM ) { + Q_strncpyz( newInfo.modelName, DEFAULT_TEAM_MODEL, sizeof( newInfo.modelName ) ); + Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) ); + } else { + trap_Cvar_VariableStringBuffer( "model", modelStr, sizeof( modelStr ) ); + if ( ( skin = strchr( modelStr, '/' ) ) == NULL) { + skin = "default"; + } else { + *skin++ = 0; + } + + Q_strncpyz( newInfo.skinName, skin, sizeof( newInfo.skinName ) ); + Q_strncpyz( newInfo.modelName, modelStr, sizeof( newInfo.modelName ) ); + } + + if ( cgs.gametype >= GT_TEAM ) { + // keep skin name + slash = strchr( v, '/' ); + if ( slash ) { + Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) ); + } + } + } else { + Q_strncpyz( newInfo.modelName, v, sizeof( newInfo.modelName ) ); + + slash = strchr( newInfo.modelName, '/' ); + if ( !slash ) { + // modelName didn not include a skin name + Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) ); + } else { + Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) ); + // truncate modelName + *slash = 0; + } + } + + // head model + v = Info_ValueForKey( configstring, "hmodel" ); + if ( cg_forceModel.integer ) { + // forcemodel makes everyone use a single model + // to prevent load hitches + char modelStr[MAX_QPATH]; + char *skin; + + if( cgs.gametype >= GT_TEAM ) { + Q_strncpyz( newInfo.headModelName, DEFAULT_TEAM_MODEL, sizeof( newInfo.headModelName ) ); + Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) ); + } else { + trap_Cvar_VariableStringBuffer( "headmodel", modelStr, sizeof( modelStr ) ); + if ( ( skin = strchr( modelStr, '/' ) ) == NULL) { + skin = "default"; + } else { + *skin++ = 0; + } + + Q_strncpyz( newInfo.headSkinName, skin, sizeof( newInfo.headSkinName ) ); + Q_strncpyz( newInfo.headModelName, modelStr, sizeof( newInfo.headModelName ) ); + } + + if ( cgs.gametype >= GT_TEAM ) { + // keep skin name + slash = strchr( v, '/' ); + if ( slash ) { + Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) ); + } + } + } else { + Q_strncpyz( newInfo.headModelName, v, sizeof( newInfo.headModelName ) ); + + slash = strchr( newInfo.headModelName, '/' ); + if ( !slash ) { + // modelName didn not include a skin name + Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) ); + } else { + Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) ); + // truncate modelName + *slash = 0; + } + } + + // scan for an existing clientinfo that matches this modelname + // so we can avoid loading checks if possible + if ( !CG_ScanForExistingClientInfo( &newInfo ) ) { + qboolean forceDefer; + + forceDefer = trap_MemoryRemaining() < 4000000; + + // if we are defering loads, just have it pick the first valid + if ( forceDefer || (cg_deferPlayers.integer && !cg_buildScript.integer && !cg.loading ) ) { + // keep whatever they had if it won't violate team skins + CG_SetDeferredClientInfo( clientNum, &newInfo ); + // if we are low on memory, leave them with this model + if ( forceDefer ) { + CG_Printf( "Memory is low. Using deferred model.\n" ); + newInfo.deferred = qfalse; + } + } else { + CG_LoadClientInfo( clientNum, &newInfo ); + } + } + + // replace whatever was there with the new one + newInfo.infoValid = qtrue; + *ci = newInfo; +} + + + +/* +====================== +CG_LoadDeferredPlayers + +Called each frame when a player is dead +and the scoreboard is up +so deferred players can be loaded +====================== +*/ +void CG_LoadDeferredPlayers( void ) { + int i; + clientInfo_t *ci; + + // scan for a deferred player to load + for ( i = 0, ci = cgs.clientinfo ; i < cgs.maxclients ; i++, ci++ ) { + if ( ci->infoValid && ci->deferred ) { + // if we are low on memory, leave it deferred + if ( trap_MemoryRemaining() < 4000000 ) { + CG_Printf( "Memory is low. Using deferred model.\n" ); + ci->deferred = qfalse; + continue; + } + CG_LoadClientInfo( i, ci ); +// break; + } + } +} + +/* +============================================================================= + +PLAYER ANIMATION + +============================================================================= +*/ + + +/* +=============== +CG_SetLerpFrameAnimation + +may include ANIM_TOGGLEBIT +=============== +*/ +static void CG_SetLerpFrameAnimation( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation ) { + animation_t *anim; + + lf->animationNumber = newAnimation; + newAnimation &= ~ANIM_TOGGLEBIT; + + if ( newAnimation < 0 || newAnimation >= MAX_TOTALANIMATIONS ) { + CG_Error( "Bad animation number: %i", newAnimation ); + } + + anim = &ci->animations[ newAnimation ]; + + lf->animation = anim; + lf->animationTime = lf->frameTime + anim->initialLerp; + + if ( cg_debugAnim.integer ) { + CG_Printf( "Anim: %i\n", newAnimation ); + } +} + +/* +=============== +CG_RunLerpFrame + +Sets cg.snap, cg.oldFrame, and cg.backlerp +cg.time should be between oldFrameTime and frameTime after exit +=============== +*/ +static void CG_RunLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation, float speedScale ) { + int f, numFrames; + animation_t *anim; + + // debugging tool to get no animations + if ( cg_animSpeed.integer == 0 ) { + lf->oldFrame = lf->frame = lf->backlerp = 0; + return; + } + + // see if the animation sequence is switching + if ( newAnimation != lf->animationNumber || !lf->animation ) { + CG_SetLerpFrameAnimation( ci, lf, newAnimation ); + } + + // if we have passed the current frame, move it to + // oldFrame and calculate a new frame + if ( cg.time >= lf->frameTime ) { + lf->oldFrame = lf->frame; + lf->oldFrameTime = lf->frameTime; + + // get the next frame based on the animation + anim = lf->animation; + if ( !anim->frameLerp ) { + return; // shouldn't happen + } + if ( cg.time < lf->animationTime ) { + lf->frameTime = lf->animationTime; // initial lerp + } else { + lf->frameTime = lf->oldFrameTime + anim->frameLerp; + } + f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp; + f *= speedScale; // adjust for haste, etc + + numFrames = anim->numFrames; + if (anim->flipflop) { + numFrames *= 2; + } + if ( f >= numFrames ) { + f -= numFrames; + if ( anim->loopFrames ) { + f %= anim->loopFrames; + f += anim->numFrames - anim->loopFrames; + } else { + f = numFrames - 1; + // the animation is stuck at the end, so it + // can immediately transition to another sequence + lf->frameTime = cg.time; + } + } + if ( anim->reversed ) { + lf->frame = anim->firstFrame + anim->numFrames - 1 - f; + } + else if (anim->flipflop && f>=anim->numFrames) { + lf->frame = anim->firstFrame + anim->numFrames - 1 - (f%anim->numFrames); + } + else { + lf->frame = anim->firstFrame + f; + } + if ( cg.time > lf->frameTime ) { + lf->frameTime = cg.time; + if ( cg_debugAnim.integer ) { + CG_Printf( "Clamp lf->frameTime\n"); + } + } + } + + if ( lf->frameTime > cg.time + 200 ) { + lf->frameTime = cg.time; + } + + if ( lf->oldFrameTime > cg.time ) { + lf->oldFrameTime = cg.time; + } + // calculate current lerp value + if ( lf->frameTime == lf->oldFrameTime ) { + lf->backlerp = 0; + } else { + lf->backlerp = 1.0 - (float)( cg.time - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime ); + } +} + + +/* +=============== +CG_ClearLerpFrame +=============== +*/ +static void CG_ClearLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int animationNumber ) { + lf->frameTime = lf->oldFrameTime = cg.time; + CG_SetLerpFrameAnimation( ci, lf, animationNumber ); + lf->oldFrame = lf->frame = lf->animation->firstFrame; +} + + +/* +=============== +CG_PlayerAnimation +=============== +*/ +static void CG_PlayerAnimation( centity_t *cent, int *legsOld, int *legs, float *legsBackLerp, + int *torsoOld, int *torso, float *torsoBackLerp ) { + clientInfo_t *ci; + int clientNum; + float speedScale; + + clientNum = cent->currentState.clientNum; + + if ( cg_noPlayerAnims.integer ) { + *legsOld = *legs = *torsoOld = *torso = 0; + return; + } + + if ( cent->currentState.powerups & ( 1 << PW_HASTE ) ) { + speedScale = 1.5; + } else { + speedScale = 1; + } + + ci = &cgs.clientinfo[ clientNum ]; + + // do the shuffle turn frames locally + if ( cent->pe.legs.yawing && ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_IDLE ) { + CG_RunLerpFrame( ci, ¢->pe.legs, LEGS_TURN, speedScale ); + } else { + CG_RunLerpFrame( ci, ¢->pe.legs, cent->currentState.legsAnim, speedScale ); + } + + *legsOld = cent->pe.legs.oldFrame; + *legs = cent->pe.legs.frame; + *legsBackLerp = cent->pe.legs.backlerp; + + CG_RunLerpFrame( ci, ¢->pe.torso, cent->currentState.torsoAnim, speedScale ); + + *torsoOld = cent->pe.torso.oldFrame; + *torso = cent->pe.torso.frame; + *torsoBackLerp = cent->pe.torso.backlerp; +} + +/* +============================================================================= + +PLAYER ANGLES + +============================================================================= +*/ + +/* +================== +CG_SwingAngles +================== +*/ +static void CG_SwingAngles( float destination, float swingTolerance, float clampTolerance, + float speed, float *angle, qboolean *swinging ) { + float swing; + float move; + float scale; + + if ( !*swinging ) { + // see if a swing should be started + swing = AngleSubtract( *angle, destination ); + if ( swing > swingTolerance || swing < -swingTolerance ) { + *swinging = qtrue; + } + } + + if ( !*swinging ) { + return; + } + + // modify the speed depending on the delta + // so it doesn't seem so linear + swing = AngleSubtract( destination, *angle ); + scale = fabs( swing ); + if ( scale < swingTolerance * 0.5 ) { + scale = 0.5; + } else if ( scale < swingTolerance ) { + scale = 1.0; + } else { + scale = 2.0; + } + + // swing towards the destination angle + if ( swing >= 0 ) { + move = cg.frametime * scale * speed; + if ( move >= swing ) { + move = swing; + *swinging = qfalse; + } + *angle = AngleMod( *angle + move ); + } else if ( swing < 0 ) { + move = cg.frametime * scale * -speed; + if ( move <= swing ) { + move = swing; + *swinging = qfalse; + } + *angle = AngleMod( *angle + move ); + } + + // clamp to no more than tolerance + swing = AngleSubtract( destination, *angle ); + if ( swing > clampTolerance ) { + *angle = AngleMod( destination - (clampTolerance - 1) ); + } else if ( swing < -clampTolerance ) { + *angle = AngleMod( destination + (clampTolerance - 1) ); + } +} + +/* +================= +CG_AddPainTwitch +================= +*/ +static void CG_AddPainTwitch( centity_t *cent, vec3_t torsoAngles ) { + int t; + float f; + + t = cg.time - cent->pe.painTime; + if ( t >= PAIN_TWITCH_TIME ) { + return; + } + + f = 1.0 - (float)t / PAIN_TWITCH_TIME; + + if ( cent->pe.painDirection ) { + torsoAngles[ROLL] += 20 * f; + } else { + torsoAngles[ROLL] -= 20 * f; + } +} + + +/* +=============== +CG_PlayerAngles + +Handles seperate torso motion + + legs pivot based on direction of movement + + head always looks exactly at cent->lerpAngles + + if motion < 20 degrees, show in head only + if < 45 degrees, also show in torso +=============== +*/ +static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) { + vec3_t legsAngles, torsoAngles, headAngles; + float dest; + static int movementOffsets[8] = { 0, 22, 45, -22, 0, 22, -45, -22 }; + vec3_t velocity; + float speed; + int dir, clientNum; + clientInfo_t *ci; + + VectorCopy( cent->lerpAngles, headAngles ); + headAngles[YAW] = AngleMod( headAngles[YAW] ); + VectorClear( legsAngles ); + VectorClear( torsoAngles ); + + // --------- yaw ------------- + + // allow yaw to drift a bit + if ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE + || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND ) { + // if not standing still, always point all in the same direction + cent->pe.torso.yawing = qtrue; // always center + cent->pe.torso.pitching = qtrue; // always center + cent->pe.legs.yawing = qtrue; // always center + } + + // adjust legs for movement dir + if ( cent->currentState.eFlags & EF_DEAD ) { + // don't let dead bodies twitch + dir = 0; + } else { + dir = cent->currentState.angles2[YAW]; + if ( dir < 0 || dir > 7 ) { + CG_Error( "Bad player movement angle" ); + } + } + legsAngles[YAW] = headAngles[YAW] + movementOffsets[ dir ]; + torsoAngles[YAW] = headAngles[YAW] + 0.25 * movementOffsets[ dir ]; + + // torso + CG_SwingAngles( torsoAngles[YAW], 25, 90, cg_swingSpeed.value, ¢->pe.torso.yawAngle, ¢->pe.torso.yawing ); + CG_SwingAngles( legsAngles[YAW], 40, 90, cg_swingSpeed.value, ¢->pe.legs.yawAngle, ¢->pe.legs.yawing ); + + torsoAngles[YAW] = cent->pe.torso.yawAngle; + legsAngles[YAW] = cent->pe.legs.yawAngle; + + + // --------- pitch ------------- + + // only show a fraction of the pitch angle in the torso + if ( headAngles[PITCH] > 180 ) { + dest = (-360 + headAngles[PITCH]) * 0.75f; + } else { + dest = headAngles[PITCH] * 0.75f; + } + CG_SwingAngles( dest, 15, 30, 0.1f, ¢->pe.torso.pitchAngle, ¢->pe.torso.pitching ); + torsoAngles[PITCH] = cent->pe.torso.pitchAngle; + + // + clientNum = cent->currentState.clientNum; + if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) { + ci = &cgs.clientinfo[ clientNum ]; + if ( ci->fixedtorso ) { + torsoAngles[PITCH] = 0.0f; + } + } + + // --------- roll ------------- + + + // lean towards the direction of travel + VectorCopy( cent->currentState.pos.trDelta, velocity ); + speed = VectorNormalize( velocity ); + if ( speed ) { + vec3_t axis[3]; + float side; + + speed *= 0.05f; + + AnglesToAxis( legsAngles, axis ); + side = speed * DotProduct( velocity, axis[1] ); + legsAngles[ROLL] -= side; + + side = speed * DotProduct( velocity, axis[0] ); + legsAngles[PITCH] += side; + } + + // + clientNum = cent->currentState.clientNum; + if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) { + ci = &cgs.clientinfo[ clientNum ]; + if ( ci->fixedlegs ) { + legsAngles[YAW] = torsoAngles[YAW]; + legsAngles[PITCH] = 0.0f; + legsAngles[ROLL] = 0.0f; + } + } + + // pain twitch + CG_AddPainTwitch( cent, torsoAngles ); + + // pull the angles back out of the hierarchial chain + AnglesSubtract( headAngles, torsoAngles, headAngles ); + AnglesSubtract( torsoAngles, legsAngles, torsoAngles ); + AnglesToAxis( legsAngles, legs ); + AnglesToAxis( torsoAngles, torso ); + AnglesToAxis( headAngles, head ); +} + + +//========================================================================== + +/* +=============== +CG_HasteTrail +=============== +*/ +static void CG_HasteTrail( centity_t *cent ) { + localEntity_t *smoke; + vec3_t origin; + int anim; + + if ( cent->trailTime > cg.time ) { + return; + } + anim = cent->pe.legs.animationNumber & ~ANIM_TOGGLEBIT; + if ( anim != LEGS_RUN && anim != LEGS_BACK ) { + return; + } + + cent->trailTime += 100; + if ( cent->trailTime < cg.time ) { + cent->trailTime = cg.time; + } + + VectorCopy( cent->lerpOrigin, origin ); + origin[2] -= 16; + + smoke = CG_SmokePuff( origin, vec3_origin, + 8, + 1, 1, 1, 1, + 500, + cg.time, + 0, + 0, + cgs.media.hastePuffShader ); + + // use the optimized local entity add + smoke->leType = LE_SCALE_FADE; +} + +#ifdef MISSIONPACK +/* +=============== +CG_BreathPuffs +=============== +*/ +static void CG_BreathPuffs( centity_t *cent, refEntity_t *head) { + clientInfo_t *ci; + vec3_t up, origin; + int contents; + + ci = &cgs.clientinfo[ cent->currentState.number ]; + + if (!cg_enableBreath.integer) { + return; + } + if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson) { + return; + } + if ( cent->currentState.eFlags & EF_DEAD ) { + return; + } + contents = trap_CM_PointContents( head->origin, 0 ); + if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { + return; + } + if ( ci->breathPuffTime > cg.time ) { + return; + } + + VectorSet( up, 0, 0, 8 ); + VectorMA(head->origin, 8, head->axis[0], origin); + VectorMA(origin, -4, head->axis[2], origin); + CG_SmokePuff( origin, up, 16, 1, 1, 1, 0.66f, 1500, cg.time, cg.time + 400, LEF_PUFF_DONT_SCALE, cgs.media.shotgunSmokePuffShader ); + ci->breathPuffTime = cg.time + 2000; +} + +/* +=============== +CG_DustTrail +=============== +*/ +static void CG_DustTrail( centity_t *cent ) { + int anim; + localEntity_t *dust; + vec3_t end, vel; + trace_t tr; + + if (!cg_enableDust.integer) + return; + + if ( cent->dustTrailTime > cg.time ) { + return; + } + + anim = cent->pe.legs.animationNumber & ~ANIM_TOGGLEBIT; + if ( anim != LEGS_LANDB && anim != LEGS_LAND ) { + return; + } + + cent->dustTrailTime += 40; + if ( cent->dustTrailTime < cg.time ) { + cent->dustTrailTime = cg.time; + } + + VectorCopy(cent->currentState.pos.trBase, end); + end[2] -= 64; + CG_Trace( &tr, cent->currentState.pos.trBase, NULL, NULL, end, cent->currentState.number, MASK_PLAYERSOLID ); + + if ( !(tr.surfaceFlags & SURF_DUST) ) + return; + + VectorCopy( cent->currentState.pos.trBase, end ); + end[2] -= 16; + + VectorSet(vel, 0, 0, -30); + dust = CG_SmokePuff( end, vel, + 24, + .8f, .8f, 0.7f, 0.33f, + 500, + cg.time, + 0, + 0, + cgs.media.dustPuffShader ); +} + +#endif + +/* +=============== +CG_TrailItem +=============== +*/ +static void CG_TrailItem( centity_t *cent, qhandle_t hModel ) { + refEntity_t ent; + vec3_t angles; + vec3_t axis[3]; + + VectorCopy( cent->lerpAngles, angles ); + angles[PITCH] = 0; + angles[ROLL] = 0; + AnglesToAxis( angles, axis ); + + memset( &ent, 0, sizeof( ent ) ); + VectorMA( cent->lerpOrigin, -16, axis[0], ent.origin ); + ent.origin[2] += 16; + angles[YAW] += 90; + AnglesToAxis( angles, ent.axis ); + + ent.hModel = hModel; + trap_R_AddRefEntityToScene( &ent ); +} + + +/* +=============== +CG_PlayerFlag +=============== +*/ +static void CG_PlayerFlag( centity_t *cent, qhandle_t hSkin, refEntity_t *torso ) { + clientInfo_t *ci; + refEntity_t pole; + refEntity_t flag; + vec3_t angles, dir; + int legsAnim, flagAnim, updateangles; + float angle, d; + + // show the flag pole model + memset( &pole, 0, sizeof(pole) ); + pole.hModel = cgs.media.flagPoleModel; + VectorCopy( torso->lightingOrigin, pole.lightingOrigin ); + pole.shadowPlane = torso->shadowPlane; + pole.renderfx = torso->renderfx; + CG_PositionEntityOnTag( &pole, torso, torso->hModel, "tag_flag" ); + trap_R_AddRefEntityToScene( &pole ); + + // show the flag model + memset( &flag, 0, sizeof(flag) ); + flag.hModel = cgs.media.flagFlapModel; + flag.customSkin = hSkin; + VectorCopy( torso->lightingOrigin, flag.lightingOrigin ); + flag.shadowPlane = torso->shadowPlane; + flag.renderfx = torso->renderfx; + + VectorClear(angles); + + updateangles = qfalse; + legsAnim = cent->currentState.legsAnim & ~ANIM_TOGGLEBIT; + if( legsAnim == LEGS_IDLE || legsAnim == LEGS_IDLECR ) { + flagAnim = FLAG_STAND; + } else if ( legsAnim == LEGS_WALK || legsAnim == LEGS_WALKCR ) { + flagAnim = FLAG_STAND; + updateangles = qtrue; + } else { + flagAnim = FLAG_RUN; + updateangles = qtrue; + } + + if ( updateangles ) { + + VectorCopy( cent->currentState.pos.trDelta, dir ); + // add gravity + dir[2] += 100; + VectorNormalize( dir ); + d = DotProduct(pole.axis[2], dir); + // if there is anough movement orthogonal to the flag pole + if (fabs(d) < 0.9) { + // + d = DotProduct(pole.axis[0], dir); + if (d > 1.0f) { + d = 1.0f; + } + else if (d < -1.0f) { + d = -1.0f; + } + angle = acos(d); + + d = DotProduct(pole.axis[1], dir); + if (d < 0) { + angles[YAW] = 360 - angle * 180 / M_PI; + } + else { + angles[YAW] = angle * 180 / M_PI; + } + if (angles[YAW] < 0) + angles[YAW] += 360; + if (angles[YAW] > 360) + angles[YAW] -= 360; + + //vectoangles( cent->currentState.pos.trDelta, tmpangles ); + //angles[YAW] = tmpangles[YAW] + 45 - cent->pe.torso.yawAngle; + // change the yaw angle + CG_SwingAngles( angles[YAW], 25, 90, 0.15f, ¢->pe.flag.yawAngle, ¢->pe.flag.yawing ); + } + + /* + d = DotProduct(pole.axis[2], dir); + angle = Q_acos(d); + + d = DotProduct(pole.axis[1], dir); + if (d < 0) { + angle = 360 - angle * 180 / M_PI; + } + else { + angle = angle * 180 / M_PI; + } + if (angle > 340 && angle < 20) { + flagAnim = FLAG_RUNUP; + } + if (angle > 160 && angle < 200) { + flagAnim = FLAG_RUNDOWN; + } + */ + } + + // set the yaw angle + angles[YAW] = cent->pe.flag.yawAngle; + // lerp the flag animation frames + ci = &cgs.clientinfo[ cent->currentState.clientNum ]; + CG_RunLerpFrame( ci, ¢->pe.flag, flagAnim, 1 ); + flag.oldframe = cent->pe.flag.oldFrame; + flag.frame = cent->pe.flag.frame; + flag.backlerp = cent->pe.flag.backlerp; + + AnglesToAxis( angles, flag.axis ); + CG_PositionRotatedEntityOnTag( &flag, &pole, pole.hModel, "tag_flag" ); + + trap_R_AddRefEntityToScene( &flag ); +} + + +#ifdef MISSIONPACK +/* +=============== +CG_PlayerTokens +=============== +*/ +static void CG_PlayerTokens( centity_t *cent, int renderfx ) { + int tokens, i, j; + float angle; + refEntity_t ent; + vec3_t dir, origin; + skulltrail_t *trail; + trail = &cg.skulltrails[cent->currentState.number]; + tokens = cent->currentState.generic1; + if ( !tokens ) { + trail->numpositions = 0; + return; + } + + if ( tokens > MAX_SKULLTRAIL ) { + tokens = MAX_SKULLTRAIL; + } + + // add skulls if there are more than last time + for (i = 0; i < tokens - trail->numpositions; i++) { + for (j = trail->numpositions; j > 0; j--) { + VectorCopy(trail->positions[j-1], trail->positions[j]); + } + VectorCopy(cent->lerpOrigin, trail->positions[0]); + } + trail->numpositions = tokens; + + // move all the skulls along the trail + VectorCopy(cent->lerpOrigin, origin); + for (i = 0; i < trail->numpositions; i++) { + VectorSubtract(trail->positions[i], origin, dir); + if (VectorNormalize(dir) > 30) { + VectorMA(origin, 30, dir, trail->positions[i]); + } + VectorCopy(trail->positions[i], origin); + } + + memset( &ent, 0, sizeof( ent ) ); + if( cgs.clientinfo[ cent->currentState.clientNum ].team == TEAM_BLUE ) { + ent.hModel = cgs.media.redCubeModel; + } else { + ent.hModel = cgs.media.blueCubeModel; + } + ent.renderfx = renderfx; + + VectorCopy(cent->lerpOrigin, origin); + for (i = 0; i < trail->numpositions; i++) { + VectorSubtract(origin, trail->positions[i], ent.axis[0]); + ent.axis[0][2] = 0; + VectorNormalize(ent.axis[0]); + VectorSet(ent.axis[2], 0, 0, 1); + CrossProduct(ent.axis[0], ent.axis[2], ent.axis[1]); + + VectorCopy(trail->positions[i], ent.origin); + angle = (((cg.time + 500 * MAX_SKULLTRAIL - 500 * i) / 16) & 255) * (M_PI * 2) / 255; + ent.origin[2] += sin(angle) * 10; + trap_R_AddRefEntityToScene( &ent ); + VectorCopy(trail->positions[i], origin); + } +} +#endif + + +/* +=============== +CG_PlayerPowerups +=============== +*/ +static void CG_PlayerPowerups( centity_t *cent, refEntity_t *torso ) { + int powerups; + clientInfo_t *ci; + + powerups = cent->currentState.powerups; + if ( !powerups ) { + return; + } + + // quad gives a dlight + if ( powerups & ( 1 << PW_QUAD ) ) { + trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2f, 0.2f, 1 ); + } + + // flight plays a looped sound + if ( powerups & ( 1 << PW_FLIGHT ) ) { + trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.flightSound ); + } + + ci = &cgs.clientinfo[ cent->currentState.clientNum ]; + // redflag + if ( powerups & ( 1 << PW_REDFLAG ) ) { + if (ci->newAnims) { + CG_PlayerFlag( cent, cgs.media.redFlagFlapSkin, torso ); + } + else { + CG_TrailItem( cent, cgs.media.redFlagModel ); + } + trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1.0, 0.2f, 0.2f ); + } + + // blueflag + if ( powerups & ( 1 << PW_BLUEFLAG ) ) { + if (ci->newAnims){ + CG_PlayerFlag( cent, cgs.media.blueFlagFlapSkin, torso ); + } + else { + CG_TrailItem( cent, cgs.media.blueFlagModel ); + } + trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2f, 0.2f, 1.0 ); + } + + // neutralflag + if ( powerups & ( 1 << PW_NEUTRALFLAG ) ) { + if (ci->newAnims) { + CG_PlayerFlag( cent, cgs.media.neutralFlagFlapSkin, torso ); + } + else { + CG_TrailItem( cent, cgs.media.neutralFlagModel ); + } + trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1.0, 1.0, 1.0 ); + } + + // haste leaves smoke trails + if ( powerups & ( 1 << PW_HASTE ) ) { + CG_HasteTrail( cent ); + } +} + + +/* +=============== +CG_PlayerFloatSprite + +Float a sprite over the player's head +=============== +*/ +static void CG_PlayerFloatSprite( centity_t *cent, qhandle_t shader ) { + int rf; + refEntity_t ent; + + if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) { + rf = RF_THIRD_PERSON; // only show in mirrors + } else { + rf = 0; + } + + memset( &ent, 0, sizeof( ent ) ); + VectorCopy( cent->lerpOrigin, ent.origin ); + ent.origin[2] += 48; + ent.reType = RT_SPRITE; + ent.customShader = shader; + ent.radius = 10; + ent.renderfx = rf; + ent.shaderRGBA[0] = 255; + ent.shaderRGBA[1] = 255; + ent.shaderRGBA[2] = 255; + ent.shaderRGBA[3] = 255; + trap_R_AddRefEntityToScene( &ent ); +} + + + +/* +=============== +CG_PlayerSprites + +Float sprites over the player's head +=============== +*/ +static void CG_PlayerSprites( centity_t *cent ) { + int team; + + if ( cent->currentState.eFlags & EF_CONNECTION ) { + CG_PlayerFloatSprite( cent, cgs.media.connectionShader ); + return; + } + + if ( cent->currentState.eFlags & EF_TALK ) { + CG_PlayerFloatSprite( cent, cgs.media.balloonShader ); + return; + } + + if ( cent->currentState.eFlags & EF_AWARD_IMPRESSIVE ) { + CG_PlayerFloatSprite( cent, cgs.media.medalImpressive ); + return; + } + + if ( cent->currentState.eFlags & EF_AWARD_EXCELLENT ) { + CG_PlayerFloatSprite( cent, cgs.media.medalExcellent ); + return; + } + + if ( cent->currentState.eFlags & EF_AWARD_GAUNTLET ) { + CG_PlayerFloatSprite( cent, cgs.media.medalGauntlet ); + return; + } + + if ( cent->currentState.eFlags & EF_AWARD_DEFEND ) { + CG_PlayerFloatSprite( cent, cgs.media.medalDefend ); + return; + } + + if ( cent->currentState.eFlags & EF_AWARD_ASSIST ) { + CG_PlayerFloatSprite( cent, cgs.media.medalAssist ); + return; + } + + if ( cent->currentState.eFlags & EF_AWARD_CAP ) { + CG_PlayerFloatSprite( cent, cgs.media.medalCapture ); + return; + } + + team = cgs.clientinfo[ cent->currentState.clientNum ].team; + if ( !(cent->currentState.eFlags & EF_DEAD) && + cg.snap->ps.persistant[PERS_TEAM] == team && + cgs.gametype >= GT_TEAM) { + if (cg_drawFriend.integer) { + CG_PlayerFloatSprite( cent, cgs.media.friendShader ); + } + return; + } +} + +/* +=============== +CG_PlayerShadow + +Returns the Z component of the surface being shadowed + + should it return a full plane instead of a Z? +=============== +*/ +#define SHADOW_DISTANCE 128 +static qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane ) { + vec3_t end, mins = {-15, -15, 0}, maxs = {15, 15, 2}; + trace_t trace; + float alpha; + + *shadowPlane = 0; + + if ( cg_shadows.integer == 0 ) { + return qfalse; + } + + // no shadows when invisible + if ( cent->currentState.powerups & ( 1 << PW_INVIS ) ) { + return qfalse; + } + + // send a trace down from the player to the ground + VectorCopy( cent->lerpOrigin, end ); + end[2] -= SHADOW_DISTANCE; + + trap_CM_BoxTrace( &trace, cent->lerpOrigin, end, mins, maxs, 0, MASK_PLAYERSOLID ); + + // no shadow if too high + if ( trace.fraction == 1.0 || trace.startsolid || trace.allsolid ) { + return qfalse; + } + + *shadowPlane = trace.endpos[2] + 1; + + if ( cg_shadows.integer != 1 ) { // no mark for stencil or projection shadows + return qtrue; + } + + // fade the shadow out with height + alpha = 1.0 - trace.fraction; + + // hack / FPE - bogus planes? + //assert( DotProduct( trace.plane.normal, trace.plane.normal ) != 0.0f ) + + // add the mark as a temporary, so it goes directly to the renderer + // without taking a spot in the cg_marks array + CG_ImpactMark( cgs.media.shadowMarkShader, trace.endpos, trace.plane.normal, + cent->pe.legs.yawAngle, alpha,alpha,alpha,1, qfalse, 24, qtrue ); + + return qtrue; +} + + +/* +=============== +CG_PlayerSplash + +Draw a mark at the water surface +=============== +*/ +static void CG_PlayerSplash( centity_t *cent ) { + vec3_t start, end; + trace_t trace; + int contents; + polyVert_t verts[4]; + + if ( !cg_shadows.integer ) { + return; + } + + VectorCopy( cent->lerpOrigin, end ); + end[2] -= 24; + + // if the feet aren't in liquid, don't make a mark + // this won't handle moving water brushes, but they wouldn't draw right anyway... + contents = trap_CM_PointContents( end, 0 ); + if ( !( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) ) { + return; + } + + VectorCopy( cent->lerpOrigin, start ); + start[2] += 32; + + // if the head isn't out of liquid, don't make a mark + contents = trap_CM_PointContents( start, 0 ); + if ( contents & ( CONTENTS_SOLID | CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { + return; + } + + // trace down to find the surface + trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ); + + if ( trace.fraction == 1.0 ) { + return; + } + + // create a mark polygon + VectorCopy( trace.endpos, verts[0].xyz ); + verts[0].xyz[0] -= 32; + verts[0].xyz[1] -= 32; + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255; + + VectorCopy( trace.endpos, verts[1].xyz ); + verts[1].xyz[0] -= 32; + verts[1].xyz[1] += 32; + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255; + + VectorCopy( trace.endpos, verts[2].xyz ); + verts[2].xyz[0] += 32; + verts[2].xyz[1] += 32; + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255; + + VectorCopy( trace.endpos, verts[3].xyz ); + verts[3].xyz[0] += 32; + verts[3].xyz[1] -= 32; + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255; + + trap_R_AddPolyToScene( cgs.media.wakeMarkShader, 4, verts ); +} + + + +/* +=============== +CG_AddRefEntityWithPowerups + +Adds a piece with modifications or duplications for powerups +Also called by CG_Missile for quad rockets, but nobody can tell... +=============== +*/ +void CG_AddRefEntityWithPowerups( refEntity_t *ent, entityState_t *state, int team ) { + + if ( state->powerups & ( 1 << PW_INVIS ) ) { + ent->customShader = cgs.media.invisShader; + trap_R_AddRefEntityToScene( ent ); + } else { + /* + if ( state->eFlags & EF_KAMIKAZE ) { + if (team == TEAM_BLUE) + ent->customShader = cgs.media.blueKamikazeShader; + else + ent->customShader = cgs.media.redKamikazeShader; + trap_R_AddRefEntityToScene( ent ); + } + else {*/ + trap_R_AddRefEntityToScene( ent ); + //} + + if ( state->powerups & ( 1 << PW_QUAD ) ) + { + if (team == TEAM_RED) + ent->customShader = cgs.media.redQuadShader; + else + ent->customShader = cgs.media.quadShader; + trap_R_AddRefEntityToScene( ent ); + } + if ( state->powerups & ( 1 << PW_REGEN ) ) { + if ( ( ( cg.time / 100 ) % 10 ) == 1 ) { + ent->customShader = cgs.media.regenShader; + trap_R_AddRefEntityToScene( ent ); + } + } + if ( state->powerups & ( 1 << PW_BATTLESUIT ) ) { + ent->customShader = cgs.media.battleSuitShader; + trap_R_AddRefEntityToScene( ent ); + } + } +} + +/* +================= +CG_LightVerts +================= +*/ +int CG_LightVerts( vec3_t normal, int numVerts, polyVert_t *verts ) +{ + int i, j; + float incoming; + vec3_t ambientLight; + vec3_t lightDir; + vec3_t directedLight; + + trap_R_LightForPoint( verts[0].xyz, ambientLight, directedLight, lightDir ); + + for (i = 0; i < numVerts; i++) { + incoming = DotProduct (normal, lightDir); + if ( incoming <= 0 ) { + verts[i].modulate[0] = ambientLight[0]; + verts[i].modulate[1] = ambientLight[1]; + verts[i].modulate[2] = ambientLight[2]; + verts[i].modulate[3] = 255; + continue; + } + j = ( ambientLight[0] + incoming * directedLight[0] ); + if ( j > 255 ) { + j = 255; + } + verts[i].modulate[0] = j; + + j = ( ambientLight[1] + incoming * directedLight[1] ); + if ( j > 255 ) { + j = 255; + } + verts[i].modulate[1] = j; + + j = ( ambientLight[2] + incoming * directedLight[2] ); + if ( j > 255 ) { + j = 255; + } + verts[i].modulate[2] = j; + + verts[i].modulate[3] = 255; + } + return qtrue; +} + +/* +=============== +CG_Player +=============== +*/ +void CG_Player( centity_t *cent ) { + clientInfo_t *ci; + refEntity_t legs; + refEntity_t torso; + refEntity_t head; + int clientNum; + int renderfx; + qboolean shadow; + float shadowPlane; +#ifdef MISSIONPACK + refEntity_t skull; + refEntity_t powerup; + int t; + float c; + float angle; + vec3_t dir, angles; +#endif + + // the client number is stored in clientNum. It can't be derived + // from the entity number, because a single client may have + // multiple corpses on the level using the same clientinfo + clientNum = cent->currentState.clientNum; + if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { + CG_Error( "Bad clientNum on player entity"); + } + ci = &cgs.clientinfo[ clientNum ]; + + // it is possible to see corpses from disconnected players that may + // not have valid clientinfo + if ( !ci->infoValid ) { + return; + } + + // get the player model information + renderfx = 0; + if ( cent->currentState.number == cg.snap->ps.clientNum) { + if (!cg.renderingThirdPerson) { + renderfx = RF_THIRD_PERSON; // only draw in mirrors + } else { + if (cg_cameraMode.integer) { + return; + } + } + } + + + memset( &legs, 0, sizeof(legs) ); + memset( &torso, 0, sizeof(torso) ); + memset( &head, 0, sizeof(head) ); + + // get the rotation information + CG_PlayerAngles( cent, legs.axis, torso.axis, head.axis ); + + // get the animation state (after rotation, to allow feet shuffle) + CG_PlayerAnimation( cent, &legs.oldframe, &legs.frame, &legs.backlerp, + &torso.oldframe, &torso.frame, &torso.backlerp ); + + // add the talk baloon or disconnect icon + CG_PlayerSprites( cent ); + + // add the shadow + shadow = CG_PlayerShadow( cent, &shadowPlane ); + + // add a water splash if partially in and out of water + CG_PlayerSplash( cent ); + + if ( cg_shadows.integer == 3 && shadow ) { + renderfx |= RF_SHADOW_PLANE; + } + renderfx |= RF_LIGHTING_ORIGIN; // use the same origin for all +#ifdef MISSIONPACK + if( cgs.gametype == GT_HARVESTER ) { + CG_PlayerTokens( cent, renderfx ); + } +#endif + // + // add the legs + // + legs.hModel = ci->legsModel; + legs.customSkin = ci->legsSkin; + + VectorCopy( cent->lerpOrigin, legs.origin ); + + VectorCopy( cent->lerpOrigin, legs.lightingOrigin ); + legs.shadowPlane = shadowPlane; + legs.renderfx = renderfx; + VectorCopy (legs.origin, legs.oldorigin); // don't positionally lerp at all + + CG_AddRefEntityWithPowerups( &legs, ¢->currentState, ci->team ); + + // if the model failed, allow the default nullmodel to be displayed + if (!legs.hModel) { + return; + } + + // + // add the torso + // + torso.hModel = ci->torsoModel; + if (!torso.hModel) { + return; + } + + torso.customSkin = ci->torsoSkin; + + VectorCopy( cent->lerpOrigin, torso.lightingOrigin ); + + CG_PositionRotatedEntityOnTag( &torso, &legs, ci->legsModel, "tag_torso"); + + torso.shadowPlane = shadowPlane; + torso.renderfx = renderfx; + + CG_AddRefEntityWithPowerups( &torso, ¢->currentState, ci->team ); + +#ifdef MISSIONPACK + if ( cent->currentState.eFlags & EF_KAMIKAZE ) { + + memset( &skull, 0, sizeof(skull) ); + + VectorCopy( cent->lerpOrigin, skull.lightingOrigin ); + skull.shadowPlane = shadowPlane; + skull.renderfx = renderfx; + + if ( cent->currentState.eFlags & EF_DEAD ) { + // one skull bobbing above the dead body + angle = ((cg.time / 7) & 255) * (M_PI * 2) / 255; + if (angle > M_PI * 2) + angle -= (float)M_PI * 2; + dir[0] = sin(angle) * 20; + dir[1] = cos(angle) * 20; + angle = ((cg.time / 4) & 255) * (M_PI * 2) / 255; + dir[2] = 15 + sin(angle) * 8; + VectorAdd(torso.origin, dir, skull.origin); + + dir[2] = 0; + VectorCopy(dir, skull.axis[1]); + VectorNormalize(skull.axis[1]); + VectorSet(skull.axis[2], 0, 0, 1); + CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]); + + skull.hModel = cgs.media.kamikazeHeadModel; + trap_R_AddRefEntityToScene( &skull ); + skull.hModel = cgs.media.kamikazeHeadTrail; + trap_R_AddRefEntityToScene( &skull ); + } + else { + // three skulls spinning around the player + angle = ((cg.time / 4) & 255) * (M_PI * 2) / 255; + dir[0] = cos(angle) * 20; + dir[1] = sin(angle) * 20; + dir[2] = cos(angle) * 20; + VectorAdd(torso.origin, dir, skull.origin); + + angles[0] = sin(angle) * 30; + angles[1] = (angle * 180 / M_PI) + 90; + if (angles[1] > 360) + angles[1] -= 360; + angles[2] = 0; + AnglesToAxis( angles, skull.axis ); + + /* + dir[2] = 0; + VectorInverse(dir); + VectorCopy(dir, skull.axis[1]); + VectorNormalize(skull.axis[1]); + VectorSet(skull.axis[2], 0, 0, 1); + CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]); + */ + + skull.hModel = cgs.media.kamikazeHeadModel; + trap_R_AddRefEntityToScene( &skull ); + // flip the trail because this skull is spinning in the other direction + VectorInverse(skull.axis[1]); + skull.hModel = cgs.media.kamikazeHeadTrail; + trap_R_AddRefEntityToScene( &skull ); + + angle = ((cg.time / 4) & 255) * (M_PI * 2) / 255 + M_PI; + if (angle > M_PI * 2) + angle -= (float)M_PI * 2; + dir[0] = sin(angle) * 20; + dir[1] = cos(angle) * 20; + dir[2] = cos(angle) * 20; + VectorAdd(torso.origin, dir, skull.origin); + + angles[0] = cos(angle - 0.5 * M_PI) * 30; + angles[1] = 360 - (angle * 180 / M_PI); + if (angles[1] > 360) + angles[1] -= 360; + angles[2] = 0; + AnglesToAxis( angles, skull.axis ); + + /* + dir[2] = 0; + VectorCopy(dir, skull.axis[1]); + VectorNormalize(skull.axis[1]); + VectorSet(skull.axis[2], 0, 0, 1); + CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]); + */ + + skull.hModel = cgs.media.kamikazeHeadModel; + trap_R_AddRefEntityToScene( &skull ); + skull.hModel = cgs.media.kamikazeHeadTrail; + trap_R_AddRefEntityToScene( &skull ); + + angle = ((cg.time / 3) & 255) * (M_PI * 2) / 255 + 0.5 * M_PI; + if (angle > M_PI * 2) + angle -= (float)M_PI * 2; + dir[0] = sin(angle) * 20; + dir[1] = cos(angle) * 20; + dir[2] = 0; + VectorAdd(torso.origin, dir, skull.origin); + + VectorCopy(dir, skull.axis[1]); + VectorNormalize(skull.axis[1]); + VectorSet(skull.axis[2], 0, 0, 1); + CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]); + + skull.hModel = cgs.media.kamikazeHeadModel; + trap_R_AddRefEntityToScene( &skull ); + skull.hModel = cgs.media.kamikazeHeadTrail; + trap_R_AddRefEntityToScene( &skull ); + } + } + + if ( cent->currentState.powerups & ( 1 << PW_GUARD ) ) { + memcpy(&powerup, &torso, sizeof(torso)); + powerup.hModel = cgs.media.guardPowerupModel; + powerup.frame = 0; + powerup.oldframe = 0; + powerup.customSkin = 0; + trap_R_AddRefEntityToScene( &powerup ); + } + if ( cent->currentState.powerups & ( 1 << PW_SCOUT ) ) { + memcpy(&powerup, &torso, sizeof(torso)); + powerup.hModel = cgs.media.scoutPowerupModel; + powerup.frame = 0; + powerup.oldframe = 0; + powerup.customSkin = 0; + trap_R_AddRefEntityToScene( &powerup ); + } + if ( cent->currentState.powerups & ( 1 << PW_DOUBLER ) ) { + memcpy(&powerup, &torso, sizeof(torso)); + powerup.hModel = cgs.media.doublerPowerupModel; + powerup.frame = 0; + powerup.oldframe = 0; + powerup.customSkin = 0; + trap_R_AddRefEntityToScene( &powerup ); + } + if ( cent->currentState.powerups & ( 1 << PW_AMMOREGEN ) ) { + memcpy(&powerup, &torso, sizeof(torso)); + powerup.hModel = cgs.media.ammoRegenPowerupModel; + powerup.frame = 0; + powerup.oldframe = 0; + powerup.customSkin = 0; + trap_R_AddRefEntityToScene( &powerup ); + } + if ( cent->currentState.powerups & ( 1 << PW_INVULNERABILITY ) ) { + if ( !ci->invulnerabilityStartTime ) { + ci->invulnerabilityStartTime = cg.time; + } + ci->invulnerabilityStopTime = cg.time; + } + else { + ci->invulnerabilityStartTime = 0; + } + if ( (cent->currentState.powerups & ( 1 << PW_INVULNERABILITY ) ) || + cg.time - ci->invulnerabilityStopTime < 250 ) { + + memcpy(&powerup, &torso, sizeof(torso)); + powerup.hModel = cgs.media.invulnerabilityPowerupModel; + powerup.customSkin = 0; + // always draw + powerup.renderfx &= ~RF_THIRD_PERSON; + VectorCopy(cent->lerpOrigin, powerup.origin); + + if ( cg.time - ci->invulnerabilityStartTime < 250 ) { + c = (float) (cg.time - ci->invulnerabilityStartTime) / 250; + } + else if (cg.time - ci->invulnerabilityStopTime < 250 ) { + c = (float) (250 - (cg.time - ci->invulnerabilityStopTime)) / 250; + } + else { + c = 1; + } + VectorSet( powerup.axis[0], c, 0, 0 ); + VectorSet( powerup.axis[1], 0, c, 0 ); + VectorSet( powerup.axis[2], 0, 0, c ); + trap_R_AddRefEntityToScene( &powerup ); + } + + t = cg.time - ci->medkitUsageTime; + if ( ci->medkitUsageTime && t < 500 ) { + memcpy(&powerup, &torso, sizeof(torso)); + powerup.hModel = cgs.media.medkitUsageModel; + powerup.customSkin = 0; + // always draw + powerup.renderfx &= ~RF_THIRD_PERSON; + VectorClear(angles); + AnglesToAxis(angles, powerup.axis); + VectorCopy(cent->lerpOrigin, powerup.origin); + powerup.origin[2] += -24 + (float) t * 80 / 500; + if ( t > 400 ) { + c = (float) (t - 1000) * 0xff / 100; + powerup.shaderRGBA[0] = 0xff - c; + powerup.shaderRGBA[1] = 0xff - c; + powerup.shaderRGBA[2] = 0xff - c; + powerup.shaderRGBA[3] = 0xff - c; + } + else { + powerup.shaderRGBA[0] = 0xff; + powerup.shaderRGBA[1] = 0xff; + powerup.shaderRGBA[2] = 0xff; + powerup.shaderRGBA[3] = 0xff; + } + trap_R_AddRefEntityToScene( &powerup ); + } +#endif // MISSIONPACK + + // + // add the head + // + head.hModel = ci->headModel; + if (!head.hModel) { + return; + } + head.customSkin = ci->headSkin; + + VectorCopy( cent->lerpOrigin, head.lightingOrigin ); + + CG_PositionRotatedEntityOnTag( &head, &torso, ci->torsoModel, "tag_head"); + + head.shadowPlane = shadowPlane; + head.renderfx = renderfx; + + CG_AddRefEntityWithPowerups( &head, ¢->currentState, ci->team ); + +#ifdef MISSIONPACK + CG_BreathPuffs(cent, &head); + + CG_DustTrail(cent); +#endif + + // + // add the gun / barrel / flash + // + CG_AddPlayerWeapon( &torso, NULL, cent, ci->team ); + + // add powerups floating behind the player + CG_PlayerPowerups( cent, &torso ); +} + + +//===================================================================== + +/* +=============== +CG_ResetPlayerEntity + +A player just came into view or teleported, so reset all animation info +=============== +*/ +void CG_ResetPlayerEntity( centity_t *cent ) { + cent->errorTime = -99999; // guarantee no error decay added + cent->extrapolated = qfalse; + + CG_ClearLerpFrame( &cgs.clientinfo[ cent->currentState.clientNum ], ¢->pe.legs, cent->currentState.legsAnim ); + CG_ClearLerpFrame( &cgs.clientinfo[ cent->currentState.clientNum ], ¢->pe.torso, cent->currentState.torsoAnim ); + + BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin ); + BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles ); + + VectorCopy( cent->lerpOrigin, cent->rawOrigin ); + VectorCopy( cent->lerpAngles, cent->rawAngles ); + + memset( ¢->pe.legs, 0, sizeof( cent->pe.legs ) ); + cent->pe.legs.yawAngle = cent->rawAngles[YAW]; + cent->pe.legs.yawing = qfalse; + cent->pe.legs.pitchAngle = 0; + cent->pe.legs.pitching = qfalse; + + memset( ¢->pe.torso, 0, sizeof( cent->pe.legs ) ); + cent->pe.torso.yawAngle = cent->rawAngles[YAW]; + cent->pe.torso.yawing = qfalse; + cent->pe.torso.pitchAngle = cent->rawAngles[PITCH]; + cent->pe.torso.pitching = qfalse; + + if ( cg_debugPosition.integer ) { + CG_Printf("%i ResetPlayerEntity yaw=%i\n", cent->currentState.number, cent->pe.torso.yawAngle ); + } +} + diff --git a/reaction/engine/code/cgame/cg_playerstate.c b/reaction/engine/code/cgame/cg_playerstate.c new file mode 100644 index 00000000..4df2da60 --- /dev/null +++ b/reaction/engine/code/cgame/cg_playerstate.c @@ -0,0 +1,526 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_playerstate.c -- this file acts on changes in a new playerState_t +// With normal play, this will be done after local prediction, but when +// following another player or playing back a demo, it will be checked +// when the snapshot transitions like all the other entities + +#include "cg_local.h" + +/* +============== +CG_CheckAmmo + +If the ammo has gone low enough to generate the warning, play a sound +============== +*/ +void CG_CheckAmmo( void ) { + int i; + int total; + int previous; + int weapons; + + // see about how many seconds of ammo we have remaining + weapons = cg.snap->ps.stats[ STAT_WEAPONS ]; + total = 0; + for ( i = WP_MACHINEGUN ; i < WP_NUM_WEAPONS ; i++ ) { + if ( ! ( weapons & ( 1 << i ) ) ) { + continue; + } + switch ( i ) { + case WP_ROCKET_LAUNCHER: + case WP_GRENADE_LAUNCHER: + case WP_RAILGUN: + case WP_SHOTGUN: +#ifdef MISSIONPACK + case WP_PROX_LAUNCHER: +#endif + total += cg.snap->ps.ammo[i] * 1000; + break; + default: + total += cg.snap->ps.ammo[i] * 200; + break; + } + if ( total >= 5000 ) { + cg.lowAmmoWarning = 0; + return; + } + } + + previous = cg.lowAmmoWarning; + + if ( total == 0 ) { + cg.lowAmmoWarning = 2; + } else { + cg.lowAmmoWarning = 1; + } + + // play a sound on transitions + if ( cg.lowAmmoWarning != previous ) { + trap_S_StartLocalSound( cgs.media.noAmmoSound, CHAN_LOCAL_SOUND ); + } +} + +/* +============== +CG_DamageFeedback +============== +*/ +void CG_DamageFeedback( int yawByte, int pitchByte, int damage ) { + float left, front, up; + float kick; + int health; + float scale; + vec3_t dir; + vec3_t angles; + float dist; + float yaw, pitch; + + // show the attacking player's head and name in corner + cg.attackerTime = cg.time; + + // the lower on health you are, the greater the view kick will be + health = cg.snap->ps.stats[STAT_HEALTH]; + if ( health < 40 ) { + scale = 1; + } else { + scale = 40.0 / health; + } + kick = damage * scale; + + if (kick < 5) + kick = 5; + if (kick > 10) + kick = 10; + + // if yaw and pitch are both 255, make the damage always centered (falling, etc) + if ( yawByte == 255 && pitchByte == 255 ) { + cg.damageX = 0; + cg.damageY = 0; + cg.v_dmg_roll = 0; + cg.v_dmg_pitch = -kick; + } else { + // positional + pitch = pitchByte / 255.0 * 360; + yaw = yawByte / 255.0 * 360; + + angles[PITCH] = pitch; + angles[YAW] = yaw; + angles[ROLL] = 0; + + AngleVectors( angles, dir, NULL, NULL ); + VectorSubtract( vec3_origin, dir, dir ); + + front = DotProduct (dir, cg.refdef.viewaxis[0] ); + left = DotProduct (dir, cg.refdef.viewaxis[1] ); + up = DotProduct (dir, cg.refdef.viewaxis[2] ); + + dir[0] = front; + dir[1] = left; + dir[2] = 0; + dist = VectorLength( dir ); + if ( dist < 0.1 ) { + dist = 0.1f; + } + + cg.v_dmg_roll = kick * left; + + cg.v_dmg_pitch = -kick * front; + + if ( front <= 0.1 ) { + front = 0.1f; + } + cg.damageX = -left / front; + cg.damageY = up / dist; + } + + // clamp the position + if ( cg.damageX > 1.0 ) { + cg.damageX = 1.0; + } + if ( cg.damageX < - 1.0 ) { + cg.damageX = -1.0; + } + + if ( cg.damageY > 1.0 ) { + cg.damageY = 1.0; + } + if ( cg.damageY < - 1.0 ) { + cg.damageY = -1.0; + } + + // don't let the screen flashes vary as much + if ( kick > 10 ) { + kick = 10; + } + cg.damageValue = kick; + cg.v_dmg_time = cg.time + DAMAGE_TIME; + cg.damageTime = cg.snap->serverTime; +} + + + + +/* +================ +CG_Respawn + +A respawn happened this snapshot +================ +*/ +void CG_Respawn( void ) { + // no error decay on player movement + cg.thisFrameTeleport = qtrue; + + // display weapons available + cg.weaponSelectTime = cg.time; + + // select the weapon the server says we are using + cg.weaponSelect = cg.snap->ps.weapon; +} + +extern char *eventnames[]; + +/* +============== +CG_CheckPlayerstateEvents +============== +*/ +void CG_CheckPlayerstateEvents( playerState_t *ps, playerState_t *ops ) { + int i; + int event; + centity_t *cent; + + if ( ps->externalEvent && ps->externalEvent != ops->externalEvent ) { + cent = &cg_entities[ ps->clientNum ]; + cent->currentState.event = ps->externalEvent; + cent->currentState.eventParm = ps->externalEventParm; + CG_EntityEvent( cent, cent->lerpOrigin ); + } + + cent = &cg.predictedPlayerEntity; // cg_entities[ ps->clientNum ]; + // go through the predictable events buffer + for ( i = ps->eventSequence - MAX_PS_EVENTS ; i < ps->eventSequence ; i++ ) { + // if we have a new predictable event + if ( i >= ops->eventSequence + // or the server told us to play another event instead of a predicted event we already issued + // or something the server told us changed our prediction causing a different event + || (i > ops->eventSequence - MAX_PS_EVENTS && ps->events[i & (MAX_PS_EVENTS-1)] != ops->events[i & (MAX_PS_EVENTS-1)]) ) { + + event = ps->events[ i & (MAX_PS_EVENTS-1) ]; + cent->currentState.event = event; + cent->currentState.eventParm = ps->eventParms[ i & (MAX_PS_EVENTS-1) ]; + CG_EntityEvent( cent, cent->lerpOrigin ); + + cg.predictableEvents[ i & (MAX_PREDICTED_EVENTS-1) ] = event; + + cg.eventSequence++; + } + } +} + +/* +================== +CG_CheckChangedPredictableEvents +================== +*/ +void CG_CheckChangedPredictableEvents( playerState_t *ps ) { + int i; + int event; + centity_t *cent; + + cent = &cg.predictedPlayerEntity; + for ( i = ps->eventSequence - MAX_PS_EVENTS ; i < ps->eventSequence ; i++ ) { + // + if (i >= cg.eventSequence) { + continue; + } + // if this event is not further back in than the maximum predictable events we remember + if (i > cg.eventSequence - MAX_PREDICTED_EVENTS) { + // if the new playerstate event is different from a previously predicted one + if ( ps->events[i & (MAX_PS_EVENTS-1)] != cg.predictableEvents[i & (MAX_PREDICTED_EVENTS-1) ] ) { + + event = ps->events[ i & (MAX_PS_EVENTS-1) ]; + cent->currentState.event = event; + cent->currentState.eventParm = ps->eventParms[ i & (MAX_PS_EVENTS-1) ]; + CG_EntityEvent( cent, cent->lerpOrigin ); + + cg.predictableEvents[ i & (MAX_PREDICTED_EVENTS-1) ] = event; + + if ( cg_showmiss.integer ) { + CG_Printf("WARNING: changed predicted event\n"); + } + } + } + } +} + +/* +================== +pushReward +================== +*/ +static void pushReward(sfxHandle_t sfx, qhandle_t shader, int rewardCount) { + if (cg.rewardStack < (MAX_REWARDSTACK-1)) { + cg.rewardStack++; + cg.rewardSound[cg.rewardStack] = sfx; + cg.rewardShader[cg.rewardStack] = shader; + cg.rewardCount[cg.rewardStack] = rewardCount; + } +} + +/* +================== +CG_CheckLocalSounds +================== +*/ +void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) { + int highScore, health, armor, reward; + sfxHandle_t sfx; + + // don't play the sounds if the player just changed teams + if ( ps->persistant[PERS_TEAM] != ops->persistant[PERS_TEAM] ) { + return; + } + + // hit changes + if ( ps->persistant[PERS_HITS] > ops->persistant[PERS_HITS] ) { + armor = ps->persistant[PERS_ATTACKEE_ARMOR] & 0xff; + health = ps->persistant[PERS_ATTACKEE_ARMOR] >> 8; +#ifdef MISSIONPACK + if (armor > 50 ) { + trap_S_StartLocalSound( cgs.media.hitSoundHighArmor, CHAN_LOCAL_SOUND ); + } else if (armor || health > 100) { + trap_S_StartLocalSound( cgs.media.hitSoundLowArmor, CHAN_LOCAL_SOUND ); + } else { + trap_S_StartLocalSound( cgs.media.hitSound, CHAN_LOCAL_SOUND ); + } +#else + trap_S_StartLocalSound( cgs.media.hitSound, CHAN_LOCAL_SOUND ); +#endif + } else if ( ps->persistant[PERS_HITS] < ops->persistant[PERS_HITS] ) { + trap_S_StartLocalSound( cgs.media.hitTeamSound, CHAN_LOCAL_SOUND ); + } + + // health changes of more than -1 should make pain sounds + if ( ps->stats[STAT_HEALTH] < ops->stats[STAT_HEALTH] - 1 ) { + if ( ps->stats[STAT_HEALTH] > 0 ) { + CG_PainEvent( &cg.predictedPlayerEntity, ps->stats[STAT_HEALTH] ); + } + } + + + // if we are going into the intermission, don't start any voices + if ( cg.intermissionStarted ) { + return; + } + + // reward sounds + reward = qfalse; + if (ps->persistant[PERS_CAPTURES] != ops->persistant[PERS_CAPTURES]) { + pushReward(cgs.media.captureAwardSound, cgs.media.medalCapture, ps->persistant[PERS_CAPTURES]); + reward = qtrue; + //Com_Printf("capture\n"); + } + if (ps->persistant[PERS_IMPRESSIVE_COUNT] != ops->persistant[PERS_IMPRESSIVE_COUNT]) { +#ifdef MISSIONPACK + if (ps->persistant[PERS_IMPRESSIVE_COUNT] == 1) { + sfx = cgs.media.firstImpressiveSound; + } else { + sfx = cgs.media.impressiveSound; + } +#else + sfx = cgs.media.impressiveSound; +#endif + pushReward(sfx, cgs.media.medalImpressive, ps->persistant[PERS_IMPRESSIVE_COUNT]); + reward = qtrue; + //Com_Printf("impressive\n"); + } + if (ps->persistant[PERS_EXCELLENT_COUNT] != ops->persistant[PERS_EXCELLENT_COUNT]) { +#ifdef MISSIONPACK + if (ps->persistant[PERS_EXCELLENT_COUNT] == 1) { + sfx = cgs.media.firstExcellentSound; + } else { + sfx = cgs.media.excellentSound; + } +#else + sfx = cgs.media.excellentSound; +#endif + pushReward(sfx, cgs.media.medalExcellent, ps->persistant[PERS_EXCELLENT_COUNT]); + reward = qtrue; + //Com_Printf("excellent\n"); + } + if (ps->persistant[PERS_GAUNTLET_FRAG_COUNT] != ops->persistant[PERS_GAUNTLET_FRAG_COUNT]) { +#ifdef MISSIONPACK + if (ops->persistant[PERS_GAUNTLET_FRAG_COUNT] == 1) { + sfx = cgs.media.firstHumiliationSound; + } else { + sfx = cgs.media.humiliationSound; + } +#else + sfx = cgs.media.humiliationSound; +#endif + pushReward(sfx, cgs.media.medalGauntlet, ps->persistant[PERS_GAUNTLET_FRAG_COUNT]); + reward = qtrue; + //Com_Printf("guantlet frag\n"); + } + if (ps->persistant[PERS_DEFEND_COUNT] != ops->persistant[PERS_DEFEND_COUNT]) { + pushReward(cgs.media.defendSound, cgs.media.medalDefend, ps->persistant[PERS_DEFEND_COUNT]); + reward = qtrue; + //Com_Printf("defend\n"); + } + if (ps->persistant[PERS_ASSIST_COUNT] != ops->persistant[PERS_ASSIST_COUNT]) { + pushReward(cgs.media.assistSound, cgs.media.medalAssist, ps->persistant[PERS_ASSIST_COUNT]); + reward = qtrue; + //Com_Printf("assist\n"); + } + // if any of the player event bits changed + if (ps->persistant[PERS_PLAYEREVENTS] != ops->persistant[PERS_PLAYEREVENTS]) { + if ((ps->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_DENIEDREWARD) != + (ops->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_DENIEDREWARD)) { + trap_S_StartLocalSound( cgs.media.deniedSound, CHAN_ANNOUNCER ); + } + else if ((ps->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_GAUNTLETREWARD) != + (ops->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_GAUNTLETREWARD)) { + trap_S_StartLocalSound( cgs.media.humiliationSound, CHAN_ANNOUNCER ); + } + else if ((ps->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_HOLYSHIT) != + (ops->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_HOLYSHIT)) { + trap_S_StartLocalSound( cgs.media.holyShitSound, CHAN_ANNOUNCER ); + } + reward = qtrue; + } + + // check for flag pickup + if ( cgs.gametype >= GT_TEAM ) { + if ((ps->powerups[PW_REDFLAG] != ops->powerups[PW_REDFLAG] && ps->powerups[PW_REDFLAG]) || + (ps->powerups[PW_BLUEFLAG] != ops->powerups[PW_BLUEFLAG] && ps->powerups[PW_BLUEFLAG]) || + (ps->powerups[PW_NEUTRALFLAG] != ops->powerups[PW_NEUTRALFLAG] && ps->powerups[PW_NEUTRALFLAG]) ) + { + trap_S_StartLocalSound( cgs.media.youHaveFlagSound, CHAN_ANNOUNCER ); + } + } + + // lead changes + if (!reward) { + // + if ( !cg.warmup ) { + // never play lead changes during warmup + if ( ps->persistant[PERS_RANK] != ops->persistant[PERS_RANK] ) { + if ( cgs.gametype < GT_TEAM) { + if ( ps->persistant[PERS_RANK] == 0 ) { + CG_AddBufferedSound(cgs.media.takenLeadSound); + } else if ( ps->persistant[PERS_RANK] == RANK_TIED_FLAG ) { + CG_AddBufferedSound(cgs.media.tiedLeadSound); + } else if ( ( ops->persistant[PERS_RANK] & ~RANK_TIED_FLAG ) == 0 ) { + CG_AddBufferedSound(cgs.media.lostLeadSound); + } + } + } + } + } + + // timelimit warnings + if ( cgs.timelimit > 0 ) { + int msec; + + msec = cg.time - cgs.levelStartTime; + if ( !( cg.timelimitWarnings & 4 ) && msec > ( cgs.timelimit * 60 + 2 ) * 1000 ) { + cg.timelimitWarnings |= 1 | 2 | 4; + trap_S_StartLocalSound( cgs.media.suddenDeathSound, CHAN_ANNOUNCER ); + } + else if ( !( cg.timelimitWarnings & 2 ) && msec > (cgs.timelimit - 1) * 60 * 1000 ) { + cg.timelimitWarnings |= 1 | 2; + trap_S_StartLocalSound( cgs.media.oneMinuteSound, CHAN_ANNOUNCER ); + } + else if ( cgs.timelimit > 5 && !( cg.timelimitWarnings & 1 ) && msec > (cgs.timelimit - 5) * 60 * 1000 ) { + cg.timelimitWarnings |= 1; + trap_S_StartLocalSound( cgs.media.fiveMinuteSound, CHAN_ANNOUNCER ); + } + } + + // fraglimit warnings + if ( cgs.fraglimit > 0 && cgs.gametype < GT_CTF) { + highScore = cgs.scores1; + if ( !( cg.fraglimitWarnings & 4 ) && highScore == (cgs.fraglimit - 1) ) { + cg.fraglimitWarnings |= 1 | 2 | 4; + CG_AddBufferedSound(cgs.media.oneFragSound); + } + else if ( cgs.fraglimit > 2 && !( cg.fraglimitWarnings & 2 ) && highScore == (cgs.fraglimit - 2) ) { + cg.fraglimitWarnings |= 1 | 2; + CG_AddBufferedSound(cgs.media.twoFragSound); + } + else if ( cgs.fraglimit > 3 && !( cg.fraglimitWarnings & 1 ) && highScore == (cgs.fraglimit - 3) ) { + cg.fraglimitWarnings |= 1; + CG_AddBufferedSound(cgs.media.threeFragSound); + } + } +} + +/* +=============== +CG_TransitionPlayerState + +=============== +*/ +void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops ) { + // check for changing follow mode + if ( ps->clientNum != ops->clientNum ) { + cg.thisFrameTeleport = qtrue; + // make sure we don't get any unwanted transition effects + *ops = *ps; + } + + // damage events (player is getting wounded) + if ( ps->damageEvent != ops->damageEvent && ps->damageCount ) { + CG_DamageFeedback( ps->damageYaw, ps->damagePitch, ps->damageCount ); + } + + // respawning + if ( ps->persistant[PERS_SPAWN_COUNT] != ops->persistant[PERS_SPAWN_COUNT] ) { + CG_Respawn(); + } + + if ( cg.mapRestart ) { + CG_Respawn(); + cg.mapRestart = qfalse; + } + + if ( cg.snap->ps.pm_type != PM_INTERMISSION + && ps->persistant[PERS_TEAM] != TEAM_SPECTATOR ) { + CG_CheckLocalSounds( ps, ops ); + } + + // check for going low on ammo + CG_CheckAmmo(); + + // run events + CG_CheckPlayerstateEvents( ps, ops ); + + // smooth the ducking viewheight change + if ( ps->viewheight != ops->viewheight ) { + cg.duckChange = ps->viewheight - ops->viewheight; + cg.duckTime = cg.time; + } +} + diff --git a/reaction/engine/code/cgame/cg_predict.c b/reaction/engine/code/cgame/cg_predict.c new file mode 100644 index 00000000..46bd633e --- /dev/null +++ b/reaction/engine/code/cgame/cg_predict.c @@ -0,0 +1,628 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_predict.c -- this file generates cg.predictedPlayerState by either +// interpolating between snapshots from the server or locally predicting +// ahead the client's movement. +// It also handles local physics interaction, like fragments bouncing off walls + +#include "cg_local.h" + +static pmove_t cg_pmove; + +static int cg_numSolidEntities; +static centity_t *cg_solidEntities[MAX_ENTITIES_IN_SNAPSHOT]; +static int cg_numTriggerEntities; +static centity_t *cg_triggerEntities[MAX_ENTITIES_IN_SNAPSHOT]; + +/* +==================== +CG_BuildSolidList + +When a new cg.snap has been set, this function builds a sublist +of the entities that are actually solid, to make for more +efficient collision detection +==================== +*/ +void CG_BuildSolidList( void ) { + int i; + centity_t *cent; + snapshot_t *snap; + entityState_t *ent; + + cg_numSolidEntities = 0; + cg_numTriggerEntities = 0; + + if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) { + snap = cg.nextSnap; + } else { + snap = cg.snap; + } + + for ( i = 0 ; i < snap->numEntities ; i++ ) { + cent = &cg_entities[ snap->entities[ i ].number ]; + ent = ¢->currentState; + + if ( ent->eType == ET_ITEM || ent->eType == ET_PUSH_TRIGGER || ent->eType == ET_TELEPORT_TRIGGER ) { + cg_triggerEntities[cg_numTriggerEntities] = cent; + cg_numTriggerEntities++; + continue; + } + + if ( cent->nextState.solid ) { + cg_solidEntities[cg_numSolidEntities] = cent; + cg_numSolidEntities++; + continue; + } + } +} + +/* +==================== +CG_ClipMoveToEntities + +==================== +*/ +static void CG_ClipMoveToEntities ( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, + int skipNumber, int mask, trace_t *tr ) { + int i, x, zd, zu; + trace_t trace; + entityState_t *ent; + clipHandle_t cmodel; + vec3_t bmins, bmaxs; + vec3_t origin, angles; + centity_t *cent; + + for ( i = 0 ; i < cg_numSolidEntities ; i++ ) { + cent = cg_solidEntities[ i ]; + ent = ¢->currentState; + + if ( ent->number == skipNumber ) { + continue; + } + + if ( ent->solid == SOLID_BMODEL ) { + // special value for bmodel + cmodel = trap_CM_InlineModel( ent->modelindex ); + VectorCopy( cent->lerpAngles, angles ); + BG_EvaluateTrajectory( ¢->currentState.pos, cg.physicsTime, origin ); + } else { + // encoded bbox + x = (ent->solid & 255); + zd = ((ent->solid>>8) & 255); + zu = ((ent->solid>>16) & 255) - 32; + + bmins[0] = bmins[1] = -x; + bmaxs[0] = bmaxs[1] = x; + bmins[2] = -zd; + bmaxs[2] = zu; + + cmodel = trap_CM_TempBoxModel( bmins, bmaxs ); + VectorCopy( vec3_origin, angles ); + VectorCopy( cent->lerpOrigin, origin ); + } + + + trap_CM_TransformedBoxTrace ( &trace, start, end, + mins, maxs, cmodel, mask, origin, angles); + + if (trace.allsolid || trace.fraction < tr->fraction) { + trace.entityNum = ent->number; + *tr = trace; + } else if (trace.startsolid) { + tr->startsolid = qtrue; + } + if ( tr->allsolid ) { + return; + } + } +} + +/* +================ +CG_Trace +================ +*/ +void CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, + int skipNumber, int mask ) { + trace_t t; + + trap_CM_BoxTrace ( &t, start, end, mins, maxs, 0, mask); + t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE; + // check all other solid models + CG_ClipMoveToEntities (start, mins, maxs, end, skipNumber, mask, &t); + + *result = t; +} + +/* +================ +CG_PointContents +================ +*/ +int CG_PointContents( const vec3_t point, int passEntityNum ) { + int i; + entityState_t *ent; + centity_t *cent; + clipHandle_t cmodel; + int contents; + + contents = trap_CM_PointContents (point, 0); + + for ( i = 0 ; i < cg_numSolidEntities ; i++ ) { + cent = cg_solidEntities[ i ]; + + ent = ¢->currentState; + + if ( ent->number == passEntityNum ) { + continue; + } + + if (ent->solid != SOLID_BMODEL) { // special value for bmodel + continue; + } + + cmodel = trap_CM_InlineModel( ent->modelindex ); + if ( !cmodel ) { + continue; + } + + contents |= trap_CM_TransformedPointContents( point, cmodel, ent->origin, ent->angles ); + } + + return contents; +} + + +/* +======================== +CG_InterpolatePlayerState + +Generates cg.predictedPlayerState by interpolating between +cg.snap->player_state and cg.nextFrame->player_state +======================== +*/ +static void CG_InterpolatePlayerState( qboolean grabAngles ) { + float f; + int i; + playerState_t *out; + snapshot_t *prev, *next; + + out = &cg.predictedPlayerState; + prev = cg.snap; + next = cg.nextSnap; + + *out = cg.snap->ps; + + // if we are still allowing local input, short circuit the view angles + if ( grabAngles ) { + usercmd_t cmd; + int cmdNum; + + cmdNum = trap_GetCurrentCmdNumber(); + trap_GetUserCmd( cmdNum, &cmd ); + + PM_UpdateViewAngles( out, &cmd ); + } + + // if the next frame is a teleport, we can't lerp to it + if ( cg.nextFrameTeleport ) { + return; + } + + if ( !next || next->serverTime <= prev->serverTime ) { + return; + } + + f = (float)( cg.time - prev->serverTime ) / ( next->serverTime - prev->serverTime ); + + i = next->ps.bobCycle; + if ( i < prev->ps.bobCycle ) { + i += 256; // handle wraparound + } + out->bobCycle = prev->ps.bobCycle + f * ( i - prev->ps.bobCycle ); + + for ( i = 0 ; i < 3 ; i++ ) { + out->origin[i] = prev->ps.origin[i] + f * (next->ps.origin[i] - prev->ps.origin[i] ); + if ( !grabAngles ) { + out->viewangles[i] = LerpAngle( + prev->ps.viewangles[i], next->ps.viewangles[i], f ); + } + out->velocity[i] = prev->ps.velocity[i] + + f * (next->ps.velocity[i] - prev->ps.velocity[i] ); + } + +} + +/* +=================== +CG_TouchItem +=================== +*/ +static void CG_TouchItem( centity_t *cent ) { + gitem_t *item; + + if ( !cg_predictItems.integer ) { + return; + } + if ( !BG_PlayerTouchesItem( &cg.predictedPlayerState, ¢->currentState, cg.time ) ) { + return; + } + + // never pick an item up twice in a prediction + if ( cent->miscTime == cg.time ) { + return; + } + + if ( !BG_CanItemBeGrabbed( cgs.gametype, ¢->currentState, &cg.predictedPlayerState ) ) { + return; // can't hold it + } + + item = &bg_itemlist[ cent->currentState.modelindex ]; + + // Special case for flags. + // We don't predict touching our own flag +#ifdef MISSIONPACK + if( cgs.gametype == GT_1FCTF ) { + if( item->giTag != PW_NEUTRALFLAG ) { + return; + } + } + if( cgs.gametype == GT_CTF || cgs.gametype == GT_HARVESTER ) { +#else + if( cgs.gametype == GT_CTF ) { +#endif + if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_RED && + item->giTag == PW_REDFLAG) + return; + if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_BLUE && + item->giTag == PW_BLUEFLAG) + return; + } + + // grab it + BG_AddPredictableEventToPlayerstate( EV_ITEM_PICKUP, cent->currentState.modelindex , &cg.predictedPlayerState); + + // remove it from the frame so it won't be drawn + cent->currentState.eFlags |= EF_NODRAW; + + // don't touch it again this prediction + cent->miscTime = cg.time; + + // if its a weapon, give them some predicted ammo so the autoswitch will work + if ( item->giType == IT_WEAPON ) { + cg.predictedPlayerState.stats[ STAT_WEAPONS ] |= 1 << item->giTag; + if ( !cg.predictedPlayerState.ammo[ item->giTag ] ) { + cg.predictedPlayerState.ammo[ item->giTag ] = 1; + } + } +} + + +/* +========================= +CG_TouchTriggerPrediction + +Predict push triggers and items +========================= +*/ +static void CG_TouchTriggerPrediction( void ) { + int i; + trace_t trace; + entityState_t *ent; + clipHandle_t cmodel; + centity_t *cent; + qboolean spectator; + + // dead clients don't activate triggers + if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { + return; + } + + spectator = ( cg.predictedPlayerState.pm_type == PM_SPECTATOR ); + + if ( cg.predictedPlayerState.pm_type != PM_NORMAL && !spectator ) { + return; + } + + for ( i = 0 ; i < cg_numTriggerEntities ; i++ ) { + cent = cg_triggerEntities[ i ]; + ent = ¢->currentState; + + if ( ent->eType == ET_ITEM && !spectator ) { + CG_TouchItem( cent ); + continue; + } + + if ( ent->solid != SOLID_BMODEL ) { + continue; + } + + cmodel = trap_CM_InlineModel( ent->modelindex ); + if ( !cmodel ) { + continue; + } + + trap_CM_BoxTrace( &trace, cg.predictedPlayerState.origin, cg.predictedPlayerState.origin, + cg_pmove.mins, cg_pmove.maxs, cmodel, -1 ); + + if ( !trace.startsolid ) { + continue; + } + + if ( ent->eType == ET_TELEPORT_TRIGGER ) { + cg.hyperspace = qtrue; + } else if ( ent->eType == ET_PUSH_TRIGGER ) { + BG_TouchJumpPad( &cg.predictedPlayerState, ent ); + } + } + + // if we didn't touch a jump pad this pmove frame + if ( cg.predictedPlayerState.jumppad_frame != cg.predictedPlayerState.pmove_framecount ) { + cg.predictedPlayerState.jumppad_frame = 0; + cg.predictedPlayerState.jumppad_ent = 0; + } +} + + + +/* +================= +CG_PredictPlayerState + +Generates cg.predictedPlayerState for the current cg.time +cg.predictedPlayerState is guaranteed to be valid after exiting. + +For demo playback, this will be an interpolation between two valid +playerState_t. + +For normal gameplay, it will be the result of predicted usercmd_t on +top of the most recent playerState_t received from the server. + +Each new snapshot will usually have one or more new usercmd over the last, +but we simulate all unacknowledged commands each time, not just the new ones. +This means that on an internet connection, quite a few pmoves may be issued +each frame. + +OPTIMIZE: don't re-simulate unless the newly arrived snapshot playerState_t +differs from the predicted one. Would require saving all intermediate +playerState_t during prediction. + +We detect prediction errors and allow them to be decayed off over several frames +to ease the jerk. +================= +*/ +void CG_PredictPlayerState( void ) { + int cmdNum, current; + playerState_t oldPlayerState; + qboolean moved; + usercmd_t oldestCmd; + usercmd_t latestCmd; + + cg.hyperspace = qfalse; // will be set if touching a trigger_teleport + + // if this is the first frame we must guarantee + // predictedPlayerState is valid even if there is some + // other error condition + if ( !cg.validPPS ) { + cg.validPPS = qtrue; + cg.predictedPlayerState = cg.snap->ps; + } + + + // demo playback just copies the moves + if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) ) { + CG_InterpolatePlayerState( qfalse ); + return; + } + + // non-predicting local movement will grab the latest angles + if ( cg_nopredict.integer || cg_synchronousClients.integer ) { + CG_InterpolatePlayerState( qtrue ); + return; + } + + // prepare for pmove + cg_pmove.ps = &cg.predictedPlayerState; + cg_pmove.trace = CG_Trace; + cg_pmove.pointcontents = CG_PointContents; + if ( cg_pmove.ps->pm_type == PM_DEAD ) { + cg_pmove.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; + } + else { + cg_pmove.tracemask = MASK_PLAYERSOLID; + } + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) { + cg_pmove.tracemask &= ~CONTENTS_BODY; // spectators can fly through bodies + } + cg_pmove.noFootsteps = ( cgs.dmflags & DF_NO_FOOTSTEPS ) > 0; + + // save the state before the pmove so we can detect transitions + oldPlayerState = cg.predictedPlayerState; + + current = trap_GetCurrentCmdNumber(); + + // if we don't have the commands right after the snapshot, we + // can't accurately predict a current position, so just freeze at + // the last good position we had + cmdNum = current - CMD_BACKUP + 1; + trap_GetUserCmd( cmdNum, &oldestCmd ); + if ( oldestCmd.serverTime > cg.snap->ps.commandTime + && oldestCmd.serverTime < cg.time ) { // special check for map_restart + if ( cg_showmiss.integer ) { + CG_Printf ("exceeded PACKET_BACKUP on commands\n"); + } + return; + } + + // get the latest command so we can know which commands are from previous map_restarts + trap_GetUserCmd( current, &latestCmd ); + + // get the most recent information we have, even if + // the server time is beyond our current cg.time, + // because predicted player positions are going to + // be ahead of everything else anyway + if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) { + cg.predictedPlayerState = cg.nextSnap->ps; + cg.physicsTime = cg.nextSnap->serverTime; + } else { + cg.predictedPlayerState = cg.snap->ps; + cg.physicsTime = cg.snap->serverTime; + } + + if ( pmove_msec.integer < 8 ) { + trap_Cvar_Set("pmove_msec", "8"); + } + else if (pmove_msec.integer > 33) { + trap_Cvar_Set("pmove_msec", "33"); + } + + cg_pmove.pmove_fixed = pmove_fixed.integer;// | cg_pmove_fixed.integer; + cg_pmove.pmove_msec = pmove_msec.integer; + + // run cmds + moved = qfalse; + for ( cmdNum = current - CMD_BACKUP + 1 ; cmdNum <= current ; cmdNum++ ) { + // get the command + trap_GetUserCmd( cmdNum, &cg_pmove.cmd ); + + if ( cg_pmove.pmove_fixed ) { + PM_UpdateViewAngles( cg_pmove.ps, &cg_pmove.cmd ); + } + + // don't do anything if the time is before the snapshot player time + if ( cg_pmove.cmd.serverTime <= cg.predictedPlayerState.commandTime ) { + continue; + } + + // don't do anything if the command was from a previous map_restart + if ( cg_pmove.cmd.serverTime > latestCmd.serverTime ) { + continue; + } + + // check for a prediction error from last frame + // on a lan, this will often be the exact value + // from the snapshot, but on a wan we will have + // to predict several commands to get to the point + // we want to compare + if ( cg.predictedPlayerState.commandTime == oldPlayerState.commandTime ) { + vec3_t delta; + float len; + + if ( cg.thisFrameTeleport ) { + // a teleport will not cause an error decay + VectorClear( cg.predictedError ); + if ( cg_showmiss.integer ) { + CG_Printf( "PredictionTeleport\n" ); + } + cg.thisFrameTeleport = qfalse; + } else { + vec3_t adjusted; + CG_AdjustPositionForMover( cg.predictedPlayerState.origin, + cg.predictedPlayerState.groundEntityNum, cg.physicsTime, cg.oldTime, adjusted ); + + if ( cg_showmiss.integer ) { + if (!VectorCompare( oldPlayerState.origin, adjusted )) { + CG_Printf("prediction error\n"); + } + } + VectorSubtract( oldPlayerState.origin, adjusted, delta ); + len = VectorLength( delta ); + if ( len > 0.1 ) { + if ( cg_showmiss.integer ) { + CG_Printf("Prediction miss: %f\n", len); + } + if ( cg_errorDecay.integer ) { + int t; + float f; + + t = cg.time - cg.predictedErrorTime; + f = ( cg_errorDecay.value - t ) / cg_errorDecay.value; + if ( f < 0 ) { + f = 0; + } + if ( f > 0 && cg_showmiss.integer ) { + CG_Printf("Double prediction decay: %f\n", f); + } + VectorScale( cg.predictedError, f, cg.predictedError ); + } else { + VectorClear( cg.predictedError ); + } + VectorAdd( delta, cg.predictedError, cg.predictedError ); + cg.predictedErrorTime = cg.oldTime; + } + } + } + + // don't predict gauntlet firing, which is only supposed to happen + // when it actually inflicts damage + cg_pmove.gauntletHit = qfalse; + + if ( cg_pmove.pmove_fixed ) { + cg_pmove.cmd.serverTime = ((cg_pmove.cmd.serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer; + } + + Pmove (&cg_pmove); + + moved = qtrue; + + // add push trigger movement effects + CG_TouchTriggerPrediction(); + + // check for predictable events that changed from previous predictions + //CG_CheckChangedPredictableEvents(&cg.predictedPlayerState); + } + + if ( cg_showmiss.integer > 1 ) { + CG_Printf( "[%i : %i] ", cg_pmove.cmd.serverTime, cg.time ); + } + + if ( !moved ) { + if ( cg_showmiss.integer ) { + CG_Printf( "not moved\n" ); + } + return; + } + + // adjust for the movement of the groundentity + CG_AdjustPositionForMover( cg.predictedPlayerState.origin, + cg.predictedPlayerState.groundEntityNum, + cg.physicsTime, cg.time, cg.predictedPlayerState.origin ); + + if ( cg_showmiss.integer ) { + if (cg.predictedPlayerState.eventSequence > oldPlayerState.eventSequence + MAX_PS_EVENTS) { + CG_Printf("WARNING: dropped event\n"); + } + } + + // fire events and other transition triggered things + CG_TransitionPlayerState( &cg.predictedPlayerState, &oldPlayerState ); + + if ( cg_showmiss.integer ) { + if (cg.eventSequence > cg.predictedPlayerState.eventSequence) { + CG_Printf("WARNING: double event\n"); + cg.eventSequence = cg.predictedPlayerState.eventSequence; + } + } +} + + diff --git a/reaction/engine/code/cgame/cg_public.h b/reaction/engine/code/cgame/cg_public.h new file mode 100644 index 00000000..7449d38e --- /dev/null +++ b/reaction/engine/code/cgame/cg_public.h @@ -0,0 +1,238 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + + +#define CMD_BACKUP 64 +#define CMD_MASK (CMD_BACKUP - 1) +// allow a lot of command backups for very fast systems +// multiple commands may be combined into a single packet, so this +// needs to be larger than PACKET_BACKUP + + +#define MAX_ENTITIES_IN_SNAPSHOT 256 + +// snapshots are a view of the server at a given time + +// Snapshots are generated at regular time intervals by the server, +// but they may not be sent if a client's rate level is exceeded, or +// they may be dropped by the network. +typedef struct { + int snapFlags; // SNAPFLAG_RATE_DELAYED, etc + int ping; + + int serverTime; // server time the message is valid for (in msec) + + byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits + + playerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + entityState_t entities[MAX_ENTITIES_IN_SNAPSHOT]; // at the time of this snapshot + + int numServerCommands; // text based server commands to execute when this + int serverCommandSequence; // snapshot becomes current +} snapshot_t; + +enum { + CGAME_EVENT_NONE, + CGAME_EVENT_TEAMMENU, + CGAME_EVENT_SCOREBOARD, + CGAME_EVENT_EDITHUD +}; + + +/* +================================================================== + +functions imported from the main executable + +================================================================== +*/ + +#define CGAME_IMPORT_API_VERSION 4 + +typedef enum { + CG_PRINT, + CG_ERROR, + CG_MILLISECONDS, + CG_CVAR_REGISTER, + CG_CVAR_UPDATE, + CG_CVAR_SET, + CG_CVAR_VARIABLESTRINGBUFFER, + CG_ARGC, + CG_ARGV, + CG_ARGS, + CG_FS_FOPENFILE, + CG_FS_READ, + CG_FS_WRITE, + CG_FS_FCLOSEFILE, + CG_SENDCONSOLECOMMAND, + CG_ADDCOMMAND, + CG_SENDCLIENTCOMMAND, + CG_UPDATESCREEN, + CG_CM_LOADMAP, + CG_CM_NUMINLINEMODELS, + CG_CM_INLINEMODEL, + CG_CM_LOADMODEL, + CG_CM_TEMPBOXMODEL, + CG_CM_POINTCONTENTS, + CG_CM_TRANSFORMEDPOINTCONTENTS, + CG_CM_BOXTRACE, + CG_CM_TRANSFORMEDBOXTRACE, + CG_CM_MARKFRAGMENTS, + CG_S_STARTSOUND, + CG_S_STARTLOCALSOUND, + CG_S_CLEARLOOPINGSOUNDS, + CG_S_ADDLOOPINGSOUND, + CG_S_UPDATEENTITYPOSITION, + CG_S_RESPATIALIZE, + CG_S_REGISTERSOUND, + CG_S_STARTBACKGROUNDTRACK, + CG_R_LOADWORLDMAP, + CG_R_REGISTERMODEL, + CG_R_REGISTERSKIN, + CG_R_REGISTERSHADER, + CG_R_CLEARSCENE, + CG_R_ADDREFENTITYTOSCENE, + CG_R_ADDPOLYTOSCENE, + CG_R_ADDLIGHTTOSCENE, + CG_R_RENDERSCENE, + CG_R_SETCOLOR, + CG_R_DRAWSTRETCHPIC, + CG_R_MODELBOUNDS, + CG_R_LERPTAG, + CG_GETGLCONFIG, + CG_GETGAMESTATE, + CG_GETCURRENTSNAPSHOTNUMBER, + CG_GETSNAPSHOT, + CG_GETSERVERCOMMAND, + CG_GETCURRENTCMDNUMBER, + CG_GETUSERCMD, + CG_SETUSERCMDVALUE, + CG_R_REGISTERSHADERNOMIP, + CG_MEMORY_REMAINING, + CG_R_REGISTERFONT, + CG_KEY_ISDOWN, + CG_KEY_GETCATCHER, + CG_KEY_SETCATCHER, + CG_KEY_GETKEY, + CG_PC_ADD_GLOBAL_DEFINE, + CG_PC_LOAD_SOURCE, + CG_PC_FREE_SOURCE, + CG_PC_READ_TOKEN, + CG_PC_SOURCE_FILE_AND_LINE, + CG_S_STOPBACKGROUNDTRACK, + CG_REAL_TIME, + CG_SNAPVECTOR, + CG_REMOVECOMMAND, + CG_R_LIGHTFORPOINT, + CG_CIN_PLAYCINEMATIC, + CG_CIN_STOPCINEMATIC, + CG_CIN_RUNCINEMATIC, + CG_CIN_DRAWCINEMATIC, + CG_CIN_SETEXTENTS, + CG_R_REMAP_SHADER, + CG_S_ADDREALLOOPINGSOUND, + CG_S_STOPLOOPINGSOUND, + + CG_CM_TEMPCAPSULEMODEL, + CG_CM_CAPSULETRACE, + CG_CM_TRANSFORMEDCAPSULETRACE, + CG_R_ADDADDITIVELIGHTTOSCENE, + CG_GET_ENTITY_TOKEN, + CG_R_ADDPOLYSTOSCENE, + CG_R_INPVS, + // 1.32 + CG_FS_SEEK, + +/* + CG_LOADCAMERA, + CG_STARTCAMERA, + CG_GETCAMERAINFO, +*/ + + CG_MEMSET = 100, + CG_MEMCPY, + CG_STRNCPY, + CG_SIN, + CG_COS, + CG_ATAN2, + CG_SQRT, + CG_FLOOR, + CG_CEIL, + CG_TESTPRINTINT, + CG_TESTPRINTFLOAT, + CG_ACOS +} cgameImport_t; + + +/* +================================================================== + +functions exported to the main executable + +================================================================== +*/ + +typedef enum { + CG_INIT, +// void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ) + // called when the level loads or when the renderer is restarted + // all media should be registered at this time + // cgame will display loading status by calling SCR_Update, which + // will call CG_DrawInformation during the loading process + // reliableCommandSequence will be 0 on fresh loads, but higher for + // demos, tourney restarts, or vid_restarts + + CG_SHUTDOWN, +// void (*CG_Shutdown)( void ); + // oportunity to flush and close any open files + + CG_CONSOLE_COMMAND, +// qboolean (*CG_ConsoleCommand)( void ); + // a console command has been issued locally that is not recognized by the + // main game system. + // use Cmd_Argc() / Cmd_Argv() to read the command, return qfalse if the + // command is not known to the game + + CG_DRAW_ACTIVE_FRAME, +// void (*CG_DrawActiveFrame)( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ); + // Generates and draws a game scene and status information at the given time. + // If demoPlayback is set, local movement prediction will not be enabled + + CG_CROSSHAIR_PLAYER, +// int (*CG_CrosshairPlayer)( void ); + + CG_LAST_ATTACKER, +// int (*CG_LastAttacker)( void ); + + CG_KEY_EVENT, +// void (*CG_KeyEvent)( int key, qboolean down ); + + CG_MOUSE_EVENT, +// void (*CG_MouseEvent)( int dx, int dy ); + CG_EVENT_HANDLING +// void (*CG_EventHandling)(int type); +} cgameExport_t; + +//---------------------------------------------- diff --git a/reaction/engine/code/cgame/cg_scoreboard.c b/reaction/engine/code/cgame/cg_scoreboard.c new file mode 100644 index 00000000..00116584 --- /dev/null +++ b/reaction/engine/code/cgame/cg_scoreboard.c @@ -0,0 +1,534 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_scoreboard -- draw the scoreboard on top of the game screen +#include "cg_local.h" + + +#define SCOREBOARD_X (0) + +#define SB_HEADER 86 +#define SB_TOP (SB_HEADER+32) + +// Where the status bar starts, so we don't overwrite it +#define SB_STATUSBAR 420 + +#define SB_NORMAL_HEIGHT 40 +#define SB_INTER_HEIGHT 16 // interleaved height + +#define SB_MAXCLIENTS_NORMAL ((SB_STATUSBAR - SB_TOP) / SB_NORMAL_HEIGHT) +#define SB_MAXCLIENTS_INTER ((SB_STATUSBAR - SB_TOP) / SB_INTER_HEIGHT - 1) + +// Used when interleaved + + + +#define SB_LEFT_BOTICON_X (SCOREBOARD_X+0) +#define SB_LEFT_HEAD_X (SCOREBOARD_X+32) +#define SB_RIGHT_BOTICON_X (SCOREBOARD_X+64) +#define SB_RIGHT_HEAD_X (SCOREBOARD_X+96) +// Normal +#define SB_BOTICON_X (SCOREBOARD_X+32) +#define SB_HEAD_X (SCOREBOARD_X+64) + +#define SB_SCORELINE_X 112 + +#define SB_RATING_WIDTH (6 * BIGCHAR_WIDTH) // width 6 +#define SB_SCORE_X (SB_SCORELINE_X + BIGCHAR_WIDTH) // width 6 +#define SB_RATING_X (SB_SCORELINE_X + 6 * BIGCHAR_WIDTH) // width 6 +#define SB_PING_X (SB_SCORELINE_X + 12 * BIGCHAR_WIDTH + 8) // width 5 +#define SB_TIME_X (SB_SCORELINE_X + 17 * BIGCHAR_WIDTH + 8) // width 5 +#define SB_NAME_X (SB_SCORELINE_X + 22 * BIGCHAR_WIDTH) // width 15 + +// The new and improved score board +// +// In cases where the number of clients is high, the score board heads are interleaved +// here's the layout + +// +// 0 32 80 112 144 240 320 400 <-- pixel position +// bot head bot head score ping time name +// +// wins/losses are drawn on bot icon now + +static qboolean localClient; // true if local client has been displayed + + + /* +================= +CG_DrawScoreboard +================= +*/ +static void CG_DrawClientScore( int y, score_t *score, float *color, float fade, qboolean largeFormat ) { + char string[1024]; + vec3_t headAngles; + clientInfo_t *ci; + int iconx, headx; + + if ( score->client < 0 || score->client >= cgs.maxclients ) { + Com_Printf( "Bad score->client: %i\n", score->client ); + return; + } + + ci = &cgs.clientinfo[score->client]; + + iconx = SB_BOTICON_X + (SB_RATING_WIDTH / 2); + headx = SB_HEAD_X + (SB_RATING_WIDTH / 2); + + // draw the handicap or bot skill marker (unless player has flag) + if ( ci->powerups & ( 1 << PW_NEUTRALFLAG ) ) { + if( largeFormat ) { + CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_FREE, qfalse ); + } + else { + CG_DrawFlagModel( iconx, y, 16, 16, TEAM_FREE, qfalse ); + } + } else if ( ci->powerups & ( 1 << PW_REDFLAG ) ) { + if( largeFormat ) { + CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_RED, qfalse ); + } + else { + CG_DrawFlagModel( iconx, y, 16, 16, TEAM_RED, qfalse ); + } + } else if ( ci->powerups & ( 1 << PW_BLUEFLAG ) ) { + if( largeFormat ) { + CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_BLUE, qfalse ); + } + else { + CG_DrawFlagModel( iconx, y, 16, 16, TEAM_BLUE, qfalse ); + } + } else { + if ( ci->botSkill > 0 && ci->botSkill <= 5 ) { + if ( cg_drawIcons.integer ) { + if( largeFormat ) { + CG_DrawPic( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, cgs.media.botSkillShaders[ ci->botSkill - 1 ] ); + } + else { + CG_DrawPic( iconx, y, 16, 16, cgs.media.botSkillShaders[ ci->botSkill - 1 ] ); + } + } + } else if ( ci->handicap < 100 ) { + Com_sprintf( string, sizeof( string ), "%i", ci->handicap ); + if ( cgs.gametype == GT_TOURNAMENT ) + CG_DrawSmallStringColor( iconx, y - SMALLCHAR_HEIGHT/2, string, color ); + else + CG_DrawSmallStringColor( iconx, y, string, color ); + } + + // draw the wins / losses + if ( cgs.gametype == GT_TOURNAMENT ) { + Com_sprintf( string, sizeof( string ), "%i/%i", ci->wins, ci->losses ); + if( ci->handicap < 100 && !ci->botSkill ) { + CG_DrawSmallStringColor( iconx, y + SMALLCHAR_HEIGHT/2, string, color ); + } + else { + CG_DrawSmallStringColor( iconx, y, string, color ); + } + } + } + + // draw the face + VectorClear( headAngles ); + headAngles[YAW] = 180; + if( largeFormat ) { + CG_DrawHead( headx, y - ( ICON_SIZE - BIGCHAR_HEIGHT ) / 2, ICON_SIZE, ICON_SIZE, + score->client, headAngles ); + } + else { + CG_DrawHead( headx, y, 16, 16, score->client, headAngles ); + } + +#ifdef MISSIONPACK + // draw the team task + if ( ci->teamTask != TEAMTASK_NONE ) { + if ( ci->teamTask == TEAMTASK_OFFENSE ) { + CG_DrawPic( headx + 48, y, 16, 16, cgs.media.assaultShader ); + } + else if ( ci->teamTask == TEAMTASK_DEFENSE ) { + CG_DrawPic( headx + 48, y, 16, 16, cgs.media.defendShader ); + } + } +#endif + // draw the score line + if ( score->ping == -1 ) { + Com_sprintf(string, sizeof(string), + " connecting %s", ci->name); + } else if ( ci->team == TEAM_SPECTATOR ) { + Com_sprintf(string, sizeof(string), + " SPECT %3i %4i %s", score->ping, score->time, ci->name); + } else { + Com_sprintf(string, sizeof(string), + "%5i %4i %4i %s", score->score, score->ping, score->time, ci->name); + } + + // highlight your position + if ( score->client == cg.snap->ps.clientNum ) { + float hcolor[4]; + int rank; + + localClient = qtrue; + + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR + || cgs.gametype >= GT_TEAM ) { + rank = -1; + } else { + rank = cg.snap->ps.persistant[PERS_RANK] & ~RANK_TIED_FLAG; + } + if ( rank == 0 ) { + hcolor[0] = 0; + hcolor[1] = 0; + hcolor[2] = 0.7f; + } else if ( rank == 1 ) { + hcolor[0] = 0.7f; + hcolor[1] = 0; + hcolor[2] = 0; + } else if ( rank == 2 ) { + hcolor[0] = 0.7f; + hcolor[1] = 0.7f; + hcolor[2] = 0; + } else { + hcolor[0] = 0.7f; + hcolor[1] = 0.7f; + hcolor[2] = 0.7f; + } + + hcolor[3] = fade * 0.7; + CG_FillRect( SB_SCORELINE_X + BIGCHAR_WIDTH + (SB_RATING_WIDTH / 2), y, + 640 - SB_SCORELINE_X - BIGCHAR_WIDTH, BIGCHAR_HEIGHT+1, hcolor ); + } + + CG_DrawBigString( SB_SCORELINE_X + (SB_RATING_WIDTH / 2), y, string, fade ); + + // add the "ready" marker for intermission exiting + if ( cg.snap->ps.stats[ STAT_CLIENTS_READY ] & ( 1 << score->client ) ) { + CG_DrawBigStringColor( iconx, y, "READY", color ); + } +} + +/* +================= +CG_TeamScoreboard +================= +*/ +static int CG_TeamScoreboard( int y, team_t team, float fade, int maxClients, int lineHeight ) { + int i; + score_t *score; + float color[4]; + int count; + clientInfo_t *ci; + + color[0] = color[1] = color[2] = 1.0; + color[3] = fade; + + count = 0; + for ( i = 0 ; i < cg.numScores && count < maxClients ; i++ ) { + score = &cg.scores[i]; + ci = &cgs.clientinfo[ score->client ]; + + if ( team != ci->team ) { + continue; + } + + CG_DrawClientScore( y + lineHeight * count, score, color, fade, lineHeight == SB_NORMAL_HEIGHT ); + + count++; + } + + return count; +} + +/* +================= +CG_DrawScoreboard + +Draw the normal in-game scoreboard +================= +*/ +qboolean CG_DrawOldScoreboard( void ) { + int x, y, w, i, n1, n2; + float fade; + float *fadeColor; + char *s; + int maxClients; + int lineHeight; + int topBorderSize, bottomBorderSize; + + // don't draw amuthing if the menu or console is up + if ( cg_paused.integer ) { + cg.deferredPlayerLoading = 0; + return qfalse; + } + + if ( cgs.gametype == GT_SINGLE_PLAYER && cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { + cg.deferredPlayerLoading = 0; + return qfalse; + } + + // don't draw scoreboard during death while warmup up + if ( cg.warmup && !cg.showScores ) { + return qfalse; + } + + if ( cg.showScores || cg.predictedPlayerState.pm_type == PM_DEAD || + cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { + fade = 1.0; + fadeColor = colorWhite; + } else { + fadeColor = CG_FadeColor( cg.scoreFadeTime, FADE_TIME ); + + if ( !fadeColor ) { + // next time scoreboard comes up, don't print killer + cg.deferredPlayerLoading = 0; + cg.killerName[0] = 0; + return qfalse; + } + fade = *fadeColor; + } + + + // fragged by ... line + if ( cg.killerName[0] ) { + s = va("Fragged by %s", cg.killerName ); + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; + x = ( SCREEN_WIDTH - w ) / 2; + y = 40; + CG_DrawBigString( x, y, s, fade ); + } + + // current rank + if ( cgs.gametype < GT_TEAM) { + if (cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) { + s = va("%s place with %i", + CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ), + cg.snap->ps.persistant[PERS_SCORE] ); + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; + x = ( SCREEN_WIDTH - w ) / 2; + y = 60; + CG_DrawBigString( x, y, s, fade ); + } + } else { + if ( cg.teamScores[0] == cg.teamScores[1] ) { + s = va("Teams are tied at %i", cg.teamScores[0] ); + } else if ( cg.teamScores[0] >= cg.teamScores[1] ) { + s = va("Red leads %i to %i",cg.teamScores[0], cg.teamScores[1] ); + } else { + s = va("Blue leads %i to %i",cg.teamScores[1], cg.teamScores[0] ); + } + + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; + x = ( SCREEN_WIDTH - w ) / 2; + y = 60; + CG_DrawBigString( x, y, s, fade ); + } + + // scoreboard + y = SB_HEADER; + + CG_DrawPic( SB_SCORE_X + (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardScore ); + CG_DrawPic( SB_PING_X - (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardPing ); + CG_DrawPic( SB_TIME_X - (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardTime ); + CG_DrawPic( SB_NAME_X - (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardName ); + + y = SB_TOP; + + // If there are more than SB_MAXCLIENTS_NORMAL, use the interleaved scores + if ( cg.numScores > SB_MAXCLIENTS_NORMAL ) { + maxClients = SB_MAXCLIENTS_INTER; + lineHeight = SB_INTER_HEIGHT; + topBorderSize = 8; + bottomBorderSize = 16; + } else { + maxClients = SB_MAXCLIENTS_NORMAL; + lineHeight = SB_NORMAL_HEIGHT; + topBorderSize = 16; + bottomBorderSize = 16; + } + + localClient = qfalse; + + if ( cgs.gametype >= GT_TEAM ) { + // + // teamplay scoreboard + // + y += lineHeight/2; + + if ( cg.teamScores[0] >= cg.teamScores[1] ) { + n1 = CG_TeamScoreboard( y, TEAM_RED, fade, maxClients, lineHeight ); + CG_DrawTeamBackground( 0, y - topBorderSize, 640, n1 * lineHeight + bottomBorderSize, 0.33f, TEAM_RED ); + y += (n1 * lineHeight) + BIGCHAR_HEIGHT; + maxClients -= n1; + n2 = CG_TeamScoreboard( y, TEAM_BLUE, fade, maxClients, lineHeight ); + CG_DrawTeamBackground( 0, y - topBorderSize, 640, n2 * lineHeight + bottomBorderSize, 0.33f, TEAM_BLUE ); + y += (n2 * lineHeight) + BIGCHAR_HEIGHT; + maxClients -= n2; + } else { + n1 = CG_TeamScoreboard( y, TEAM_BLUE, fade, maxClients, lineHeight ); + CG_DrawTeamBackground( 0, y - topBorderSize, 640, n1 * lineHeight + bottomBorderSize, 0.33f, TEAM_BLUE ); + y += (n1 * lineHeight) + BIGCHAR_HEIGHT; + maxClients -= n1; + n2 = CG_TeamScoreboard( y, TEAM_RED, fade, maxClients, lineHeight ); + CG_DrawTeamBackground( 0, y - topBorderSize, 640, n2 * lineHeight + bottomBorderSize, 0.33f, TEAM_RED ); + y += (n2 * lineHeight) + BIGCHAR_HEIGHT; + maxClients -= n2; + } + n1 = CG_TeamScoreboard( y, TEAM_SPECTATOR, fade, maxClients, lineHeight ); + y += (n1 * lineHeight) + BIGCHAR_HEIGHT; + + } else { + // + // free for all scoreboard + // + n1 = CG_TeamScoreboard( y, TEAM_FREE, fade, maxClients, lineHeight ); + y += (n1 * lineHeight) + BIGCHAR_HEIGHT; + n2 = CG_TeamScoreboard( y, TEAM_SPECTATOR, fade, maxClients - n1, lineHeight ); + y += (n2 * lineHeight) + BIGCHAR_HEIGHT; + } + + if (!localClient) { + // draw local client at the bottom + for ( i = 0 ; i < cg.numScores ; i++ ) { + if ( cg.scores[i].client == cg.snap->ps.clientNum ) { + CG_DrawClientScore( y, &cg.scores[i], fadeColor, fade, lineHeight == SB_NORMAL_HEIGHT ); + break; + } + } + } + + // load any models that have been deferred + if ( ++cg.deferredPlayerLoading > 10 ) { + CG_LoadDeferredPlayers(); + } + + return qtrue; +} + +//================================================================================ + +/* +================ +CG_CenterGiantLine +================ +*/ +static void CG_CenterGiantLine( float y, const char *string ) { + float x; + vec4_t color; + + color[0] = 1; + color[1] = 1; + color[2] = 1; + color[3] = 1; + + x = 0.5 * ( 640 - GIANT_WIDTH * CG_DrawStrlen( string ) ); + + CG_DrawStringExt( x, y, string, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); +} + +/* +================= +CG_DrawTourneyScoreboard + +Draw the oversize scoreboard for tournements +================= +*/ +void CG_DrawOldTourneyScoreboard( void ) { + const char *s; + vec4_t color; + int min, tens, ones; + clientInfo_t *ci; + int y; + int i; + + // request more scores regularly + if ( cg.scoresRequestTime + 2000 < cg.time ) { + cg.scoresRequestTime = cg.time; + trap_SendClientCommand( "score" ); + } + + color[0] = 1; + color[1] = 1; + color[2] = 1; + color[3] = 1; + + // draw the dialog background + color[0] = color[1] = color[2] = 0; + color[3] = 1; + CG_FillRect( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, color ); + + // print the mesage of the day + s = CG_ConfigString( CS_MOTD ); + if ( !s[0] ) { + s = "Scoreboard"; + } + + // print optional title + CG_CenterGiantLine( 8, s ); + + // print server time + ones = cg.time / 1000; + min = ones / 60; + ones %= 60; + tens = ones / 10; + ones %= 10; + s = va("%i:%i%i", min, tens, ones ); + + CG_CenterGiantLine( 64, s ); + + + // print the two scores + + y = 160; + if ( cgs.gametype >= GT_TEAM ) { + // + // teamplay scoreboard + // + CG_DrawStringExt( 8, y, "Red Team", color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); + s = va("%i", cg.teamScores[0] ); + CG_DrawStringExt( 632 - GIANT_WIDTH * strlen(s), y, s, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); + + y += 64; + + CG_DrawStringExt( 8, y, "Blue Team", color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); + s = va("%i", cg.teamScores[1] ); + CG_DrawStringExt( 632 - GIANT_WIDTH * strlen(s), y, s, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); + } else { + // + // free for all scoreboard + // + for ( i = 0 ; i < MAX_CLIENTS ; i++ ) { + ci = &cgs.clientinfo[i]; + if ( !ci->infoValid ) { + continue; + } + if ( ci->team != TEAM_FREE ) { + continue; + } + + CG_DrawStringExt( 8, y, ci->name, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); + s = va("%i", ci->score ); + CG_DrawStringExt( 632 - GIANT_WIDTH * strlen(s), y, s, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); + y += 64; + } + } + + +} + diff --git a/reaction/engine/code/cgame/cg_servercmds.c b/reaction/engine/code/cgame/cg_servercmds.c new file mode 100644 index 00000000..7761646e --- /dev/null +++ b/reaction/engine/code/cgame/cg_servercmds.c @@ -0,0 +1,1121 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_servercmds.c -- reliably sequenced text commands sent by the server +// these are processed at snapshot transition time, so there will definately +// be a valid snapshot this frame + +#include "cg_local.h" +#include "../../ui/menudef.h" + +typedef struct { + const char *order; + int taskNum; +} orderTask_t; + +static const orderTask_t validOrders[] = { + { VOICECHAT_GETFLAG, TEAMTASK_OFFENSE }, + { VOICECHAT_OFFENSE, TEAMTASK_OFFENSE }, + { VOICECHAT_DEFEND, TEAMTASK_DEFENSE }, + { VOICECHAT_DEFENDFLAG, TEAMTASK_DEFENSE }, + { VOICECHAT_PATROL, TEAMTASK_PATROL }, + { VOICECHAT_CAMP, TEAMTASK_CAMP }, + { VOICECHAT_FOLLOWME, TEAMTASK_FOLLOW }, + { VOICECHAT_RETURNFLAG, TEAMTASK_RETRIEVE }, + { VOICECHAT_FOLLOWFLAGCARRIER, TEAMTASK_ESCORT } +}; + +static const int numValidOrders = sizeof(validOrders) / sizeof(orderTask_t); + +#ifdef MISSIONPACK +static int CG_ValidOrder(const char *p) { + int i; + for (i = 0; i < numValidOrders; i++) { + if (Q_stricmp(p, validOrders[i].order) == 0) { + return validOrders[i].taskNum; + } + } + return -1; +} +#endif + +/* +================= +CG_ParseScores + +================= +*/ +static void CG_ParseScores( void ) { + int i, powerups; + + cg.numScores = atoi( CG_Argv( 1 ) ); + if ( cg.numScores > MAX_CLIENTS ) { + cg.numScores = MAX_CLIENTS; + } + + cg.teamScores[0] = atoi( CG_Argv( 2 ) ); + cg.teamScores[1] = atoi( CG_Argv( 3 ) ); + + memset( cg.scores, 0, sizeof( cg.scores ) ); + for ( i = 0 ; i < cg.numScores ; i++ ) { + // + cg.scores[i].client = atoi( CG_Argv( i * 14 + 4 ) ); + cg.scores[i].score = atoi( CG_Argv( i * 14 + 5 ) ); + cg.scores[i].ping = atoi( CG_Argv( i * 14 + 6 ) ); + cg.scores[i].time = atoi( CG_Argv( i * 14 + 7 ) ); + cg.scores[i].scoreFlags = atoi( CG_Argv( i * 14 + 8 ) ); + powerups = atoi( CG_Argv( i * 14 + 9 ) ); + cg.scores[i].accuracy = atoi(CG_Argv(i * 14 + 10)); + cg.scores[i].impressiveCount = atoi(CG_Argv(i * 14 + 11)); + cg.scores[i].excellentCount = atoi(CG_Argv(i * 14 + 12)); + cg.scores[i].guantletCount = atoi(CG_Argv(i * 14 + 13)); + cg.scores[i].defendCount = atoi(CG_Argv(i * 14 + 14)); + cg.scores[i].assistCount = atoi(CG_Argv(i * 14 + 15)); + cg.scores[i].perfect = atoi(CG_Argv(i * 14 + 16)); + cg.scores[i].captures = atoi(CG_Argv(i * 14 + 17)); + + if ( cg.scores[i].client < 0 || cg.scores[i].client >= MAX_CLIENTS ) { + cg.scores[i].client = 0; + } + cgs.clientinfo[ cg.scores[i].client ].score = cg.scores[i].score; + cgs.clientinfo[ cg.scores[i].client ].powerups = powerups; + + cg.scores[i].team = cgs.clientinfo[cg.scores[i].client].team; + } +#ifdef MISSIONPACK + CG_SetScoreSelection(NULL); +#endif + +} + +/* +================= +CG_ParseTeamInfo + +================= +*/ +static void CG_ParseTeamInfo( void ) { + int i; + int client; + + numSortedTeamPlayers = atoi( CG_Argv( 1 ) ); + if( numSortedTeamPlayers < 0 || numSortedTeamPlayers > TEAM_MAXOVERLAY ) + { + CG_Error( "CG_ParseTeamInfo: numSortedTeamPlayers out of range (%d)", + numSortedTeamPlayers ); + return; + } + + for ( i = 0 ; i < numSortedTeamPlayers ; i++ ) { + client = atoi( CG_Argv( i * 6 + 2 ) ); + if( client < 0 || client >= MAX_CLIENTS ) + { + CG_Error( "CG_ParseTeamInfo: bad client number: %d", client ); + return; + } + + sortedTeamPlayers[i] = client; + + cgs.clientinfo[ client ].location = atoi( CG_Argv( i * 6 + 3 ) ); + cgs.clientinfo[ client ].health = atoi( CG_Argv( i * 6 + 4 ) ); + cgs.clientinfo[ client ].armor = atoi( CG_Argv( i * 6 + 5 ) ); + cgs.clientinfo[ client ].curWeapon = atoi( CG_Argv( i * 6 + 6 ) ); + cgs.clientinfo[ client ].powerups = atoi( CG_Argv( i * 6 + 7 ) ); + } +} + + +/* +================ +CG_ParseServerinfo + +This is called explicitly when the gamestate is first received, +and whenever the server updates any serverinfo flagged cvars +================ +*/ +void CG_ParseServerinfo( void ) { + const char *info; + char *mapname; + + info = CG_ConfigString( CS_SERVERINFO ); + cgs.gametype = atoi( Info_ValueForKey( info, "g_gametype" ) ); + trap_Cvar_Set("g_gametype", va("%i", cgs.gametype)); + cgs.dmflags = atoi( Info_ValueForKey( info, "dmflags" ) ); + cgs.teamflags = atoi( Info_ValueForKey( info, "teamflags" ) ); + cgs.fraglimit = atoi( Info_ValueForKey( info, "fraglimit" ) ); + cgs.capturelimit = atoi( Info_ValueForKey( info, "capturelimit" ) ); + cgs.timelimit = atoi( Info_ValueForKey( info, "timelimit" ) ); + cgs.maxclients = atoi( Info_ValueForKey( info, "sv_maxclients" ) ); + mapname = Info_ValueForKey( info, "mapname" ); + Com_sprintf( cgs.mapname, sizeof( cgs.mapname ), "maps/%s.bsp", mapname ); + Q_strncpyz( cgs.redTeam, Info_ValueForKey( info, "g_redTeam" ), sizeof(cgs.redTeam) ); + trap_Cvar_Set("g_redTeam", cgs.redTeam); + Q_strncpyz( cgs.blueTeam, Info_ValueForKey( info, "g_blueTeam" ), sizeof(cgs.blueTeam) ); + trap_Cvar_Set("g_blueTeam", cgs.blueTeam); +} + +/* +================== +CG_ParseWarmup +================== +*/ +static void CG_ParseWarmup( void ) { + const char *info; + int warmup; + + info = CG_ConfigString( CS_WARMUP ); + + warmup = atoi( info ); + cg.warmupCount = -1; + + if ( warmup == 0 && cg.warmup ) { + + } else if ( warmup > 0 && cg.warmup <= 0 ) { +#ifdef MISSIONPACK + if (cgs.gametype >= GT_CTF && cgs.gametype <= GT_HARVESTER) { + trap_S_StartLocalSound( cgs.media.countPrepareTeamSound, CHAN_ANNOUNCER ); + } else +#endif + { + trap_S_StartLocalSound( cgs.media.countPrepareSound, CHAN_ANNOUNCER ); + } + } + + cg.warmup = warmup; +} + +/* +================ +CG_SetConfigValues + +Called on load to set the initial values from configure strings +================ +*/ +void CG_SetConfigValues( void ) { + const char *s; + + cgs.scores1 = atoi( CG_ConfigString( CS_SCORES1 ) ); + cgs.scores2 = atoi( CG_ConfigString( CS_SCORES2 ) ); + cgs.levelStartTime = atoi( CG_ConfigString( CS_LEVEL_START_TIME ) ); + if( cgs.gametype == GT_CTF ) { + s = CG_ConfigString( CS_FLAGSTATUS ); + cgs.redflag = s[0] - '0'; + cgs.blueflag = s[1] - '0'; + } +#ifdef MISSIONPACK + else if( cgs.gametype == GT_1FCTF ) { + s = CG_ConfigString( CS_FLAGSTATUS ); + cgs.flagStatus = s[0] - '0'; + } +#endif + cg.warmup = atoi( CG_ConfigString( CS_WARMUP ) ); +} + +/* +===================== +CG_ShaderStateChanged +===================== +*/ +void CG_ShaderStateChanged(void) { + char originalShader[MAX_QPATH]; + char newShader[MAX_QPATH]; + char timeOffset[16]; + const char *o; + char *n,*t; + + o = CG_ConfigString( CS_SHADERSTATE ); + while (o && *o) { + n = strstr(o, "="); + if (n && *n) { + strncpy(originalShader, o, n-o); + originalShader[n-o] = 0; + n++; + t = strstr(n, ":"); + if (t && *t) { + strncpy(newShader, n, t-n); + newShader[t-n] = 0; + } else { + break; + } + t++; + o = strstr(t, "@"); + if (o) { + strncpy(timeOffset, t, o-t); + timeOffset[o-t] = 0; + o++; + trap_R_RemapShader( originalShader, newShader, timeOffset ); + } + } else { + break; + } + } +} + +/* +================ +CG_ConfigStringModified + +================ +*/ +static void CG_ConfigStringModified( void ) { + const char *str; + int num; + + num = atoi( CG_Argv( 1 ) ); + + // get the gamestate from the client system, which will have the + // new configstring already integrated + trap_GetGameState( &cgs.gameState ); + + // look up the individual string that was modified + str = CG_ConfigString( num ); + + // do something with it if necessary + if ( num == CS_MUSIC ) { + CG_StartMusic(); + } else if ( num == CS_SERVERINFO ) { + CG_ParseServerinfo(); + } else if ( num == CS_WARMUP ) { + CG_ParseWarmup(); + } else if ( num == CS_SCORES1 ) { + cgs.scores1 = atoi( str ); + } else if ( num == CS_SCORES2 ) { + cgs.scores2 = atoi( str ); + } else if ( num == CS_LEVEL_START_TIME ) { + cgs.levelStartTime = atoi( str ); + } else if ( num == CS_VOTE_TIME ) { + cgs.voteTime = atoi( str ); + cgs.voteModified = qtrue; + } else if ( num == CS_VOTE_YES ) { + cgs.voteYes = atoi( str ); + cgs.voteModified = qtrue; + } else if ( num == CS_VOTE_NO ) { + cgs.voteNo = atoi( str ); + cgs.voteModified = qtrue; + } else if ( num == CS_VOTE_STRING ) { + Q_strncpyz( cgs.voteString, str, sizeof( cgs.voteString ) ); +#ifdef MISSIONPACK + trap_S_StartLocalSound( cgs.media.voteNow, CHAN_ANNOUNCER ); +#endif //MISSIONPACK + } else if ( num >= CS_TEAMVOTE_TIME && num <= CS_TEAMVOTE_TIME + 1) { + cgs.teamVoteTime[num-CS_TEAMVOTE_TIME] = atoi( str ); + cgs.teamVoteModified[num-CS_TEAMVOTE_TIME] = qtrue; + } else if ( num >= CS_TEAMVOTE_YES && num <= CS_TEAMVOTE_YES + 1) { + cgs.teamVoteYes[num-CS_TEAMVOTE_YES] = atoi( str ); + cgs.teamVoteModified[num-CS_TEAMVOTE_YES] = qtrue; + } else if ( num >= CS_TEAMVOTE_NO && num <= CS_TEAMVOTE_NO + 1) { + cgs.teamVoteNo[num-CS_TEAMVOTE_NO] = atoi( str ); + cgs.teamVoteModified[num-CS_TEAMVOTE_NO] = qtrue; + } else if ( num >= CS_TEAMVOTE_STRING && num <= CS_TEAMVOTE_STRING + 1) { + Q_strncpyz( cgs.teamVoteString[num-CS_TEAMVOTE_STRING], str, sizeof( cgs.teamVoteString ) ); +#ifdef MISSIONPACK + trap_S_StartLocalSound( cgs.media.voteNow, CHAN_ANNOUNCER ); +#endif + } else if ( num == CS_INTERMISSION ) { + cg.intermissionStarted = atoi( str ); + } else if ( num >= CS_MODELS && num < CS_MODELS+MAX_MODELS ) { + cgs.gameModels[ num-CS_MODELS ] = trap_R_RegisterModel( str ); + } else if ( num >= CS_SOUNDS && num < CS_SOUNDS+MAX_SOUNDS ) { + if ( str[0] != '*' ) { // player specific sounds don't register here + cgs.gameSounds[ num-CS_SOUNDS] = trap_S_RegisterSound( str, qfalse ); + } + } else if ( num >= CS_PLAYERS && num < CS_PLAYERS+MAX_CLIENTS ) { + CG_NewClientInfo( num - CS_PLAYERS ); + CG_BuildSpectatorString(); + } else if ( num == CS_FLAGSTATUS ) { + if( cgs.gametype == GT_CTF ) { + // format is rb where its red/blue, 0 is at base, 1 is taken, 2 is dropped + cgs.redflag = str[0] - '0'; + cgs.blueflag = str[1] - '0'; + } +#ifdef MISSIONPACK + else if( cgs.gametype == GT_1FCTF ) { + cgs.flagStatus = str[0] - '0'; + } +#endif + } + else if ( num == CS_SHADERSTATE ) { + CG_ShaderStateChanged(); + } + +} + + +/* +======================= +CG_AddToTeamChat + +======================= +*/ +static void CG_AddToTeamChat( const char *str ) { + int len; + char *p, *ls; + int lastcolor; + int chatHeight; + + if (cg_teamChatHeight.integer < TEAMCHAT_HEIGHT) { + chatHeight = cg_teamChatHeight.integer; + } else { + chatHeight = TEAMCHAT_HEIGHT; + } + + if (chatHeight <= 0 || cg_teamChatTime.integer <= 0) { + // team chat disabled, dump into normal chat + cgs.teamChatPos = cgs.teamLastChatPos = 0; + return; + } + + len = 0; + + p = cgs.teamChatMsgs[cgs.teamChatPos % chatHeight]; + *p = 0; + + lastcolor = '7'; + + ls = NULL; + while (*str) { + if (len > TEAMCHAT_WIDTH - 1) { + if (ls) { + str -= (p - ls); + str++; + p -= (p - ls); + } + *p = 0; + + cgs.teamChatMsgTimes[cgs.teamChatPos % chatHeight] = cg.time; + + cgs.teamChatPos++; + p = cgs.teamChatMsgs[cgs.teamChatPos % chatHeight]; + *p = 0; + *p++ = Q_COLOR_ESCAPE; + *p++ = lastcolor; + len = 0; + ls = NULL; + } + + if ( Q_IsColorString( str ) ) { + *p++ = *str++; + lastcolor = *str; + *p++ = *str++; + continue; + } + if (*str == ' ') { + ls = p; + } + *p++ = *str++; + len++; + } + *p = 0; + + cgs.teamChatMsgTimes[cgs.teamChatPos % chatHeight] = cg.time; + cgs.teamChatPos++; + + if (cgs.teamChatPos - cgs.teamLastChatPos > chatHeight) + cgs.teamLastChatPos = cgs.teamChatPos - chatHeight; +} + +/* +=============== +CG_MapRestart + +The server has issued a map_restart, so the next snapshot +is completely new and should not be interpolated to. + +A tournement restart will clear everything, but doesn't +require a reload of all the media +=============== +*/ +static void CG_MapRestart( void ) { + if ( cg_showmiss.integer ) { + CG_Printf( "CG_MapRestart\n" ); + } + + CG_InitLocalEntities(); + CG_InitMarkPolys(); + CG_ClearParticles (); + + // make sure the "3 frags left" warnings play again + cg.fraglimitWarnings = 0; + + cg.timelimitWarnings = 0; + + cg.intermissionStarted = qfalse; + + cgs.voteTime = 0; + + cg.mapRestart = qtrue; + + CG_StartMusic(); + + trap_S_ClearLoopingSounds(qtrue); + + // we really should clear more parts of cg here and stop sounds + + // play the "fight" sound if this is a restart without warmup + if ( cg.warmup == 0 /* && cgs.gametype == GT_TOURNAMENT */) { + trap_S_StartLocalSound( cgs.media.countFightSound, CHAN_ANNOUNCER ); + CG_CenterPrint( "FIGHT!", 120, GIANTCHAR_WIDTH*2 ); + } +#ifdef MISSIONPACK + if (cg_singlePlayerActive.integer) { + trap_Cvar_Set("ui_matchStartTime", va("%i", cg.time)); + if (cg_recordSPDemo.integer && cg_recordSPDemoName.string && *cg_recordSPDemoName.string) { + trap_SendConsoleCommand(va("set g_synchronousclients 1 ; record %s \n", cg_recordSPDemoName.string)); + } + } +#endif + trap_Cvar_Set("cg_thirdPerson", "0"); +} + +#define MAX_VOICEFILESIZE 16384 +#define MAX_VOICEFILES 8 +#define MAX_VOICECHATS 64 +#define MAX_VOICESOUNDS 64 +#define MAX_CHATSIZE 64 +#define MAX_HEADMODELS 64 + +typedef struct voiceChat_s +{ + char id[64]; + int numSounds; + sfxHandle_t sounds[MAX_VOICESOUNDS]; + char chats[MAX_VOICESOUNDS][MAX_CHATSIZE]; +} voiceChat_t; + +typedef struct voiceChatList_s +{ + char name[64]; + int gender; + int numVoiceChats; + voiceChat_t voiceChats[MAX_VOICECHATS]; +} voiceChatList_t; + +typedef struct headModelVoiceChat_s +{ + char headmodel[64]; + int voiceChatNum; +} headModelVoiceChat_t; + +voiceChatList_t voiceChatLists[MAX_VOICEFILES]; +headModelVoiceChat_t headModelVoiceChat[MAX_HEADMODELS]; + +/* +================= +CG_ParseVoiceChats +================= +*/ +int CG_ParseVoiceChats( const char *filename, voiceChatList_t *voiceChatList, int maxVoiceChats ) { + int len, i; + fileHandle_t f; + char buf[MAX_VOICEFILESIZE]; + char **p, *ptr; + char *token; + voiceChat_t *voiceChats; + qboolean compress; + sfxHandle_t sound; + + compress = qtrue; + if (cg_buildScript.integer) { + compress = qfalse; + } + + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + if ( !f ) { + trap_Print( va( S_COLOR_RED "voice chat file not found: %s\n", filename ) ); + return qfalse; + } + if ( len >= MAX_VOICEFILESIZE ) { + trap_Print( va( S_COLOR_RED "voice chat file too large: %s is %i, max allowed is %i", filename, len, MAX_VOICEFILESIZE ) ); + trap_FS_FCloseFile( f ); + return qfalse; + } + + trap_FS_Read( buf, len, f ); + buf[len] = 0; + trap_FS_FCloseFile( f ); + + ptr = buf; + p = &ptr; + + Com_sprintf(voiceChatList->name, sizeof(voiceChatList->name), "%s", filename); + voiceChats = voiceChatList->voiceChats; + for ( i = 0; i < maxVoiceChats; i++ ) { + voiceChats[i].id[0] = 0; + } + token = COM_ParseExt(p, qtrue); + if (!token || token[0] == 0) { + return qtrue; + } + if (!Q_stricmp(token, "female")) { + voiceChatList->gender = GENDER_FEMALE; + } + else if (!Q_stricmp(token, "male")) { + voiceChatList->gender = GENDER_MALE; + } + else if (!Q_stricmp(token, "neuter")) { + voiceChatList->gender = GENDER_NEUTER; + } + else { + trap_Print( va( S_COLOR_RED "expected gender not found in voice chat file: %s\n", filename ) ); + return qfalse; + } + + voiceChatList->numVoiceChats = 0; + while ( 1 ) { + token = COM_ParseExt(p, qtrue); + if (!token || token[0] == 0) { + return qtrue; + } + Com_sprintf(voiceChats[voiceChatList->numVoiceChats].id, sizeof( voiceChats[voiceChatList->numVoiceChats].id ), "%s", token); + token = COM_ParseExt(p, qtrue); + if (Q_stricmp(token, "{")) { + trap_Print( va( S_COLOR_RED "expected { found %s in voice chat file: %s\n", token, filename ) ); + return qfalse; + } + voiceChats[voiceChatList->numVoiceChats].numSounds = 0; + while(1) { + token = COM_ParseExt(p, qtrue); + if (!token || token[0] == 0) { + return qtrue; + } + if (!Q_stricmp(token, "}")) + break; + sound = trap_S_RegisterSound( token, compress ); + voiceChats[voiceChatList->numVoiceChats].sounds[voiceChats[voiceChatList->numVoiceChats].numSounds] = sound; + token = COM_ParseExt(p, qtrue); + if (!token || token[0] == 0) { + return qtrue; + } + Com_sprintf(voiceChats[voiceChatList->numVoiceChats].chats[ + voiceChats[voiceChatList->numVoiceChats].numSounds], MAX_CHATSIZE, "%s", token); + if (sound) + voiceChats[voiceChatList->numVoiceChats].numSounds++; + if (voiceChats[voiceChatList->numVoiceChats].numSounds >= MAX_VOICESOUNDS) + break; + } + voiceChatList->numVoiceChats++; + if (voiceChatList->numVoiceChats >= maxVoiceChats) + return qtrue; + } + return qtrue; +} + +/* +================= +CG_LoadVoiceChats +================= +*/ +void CG_LoadVoiceChats( void ) { + int size; + + size = trap_MemoryRemaining(); + CG_ParseVoiceChats( "scripts/female1.voice", &voiceChatLists[0], MAX_VOICECHATS ); + CG_ParseVoiceChats( "scripts/female2.voice", &voiceChatLists[1], MAX_VOICECHATS ); + CG_ParseVoiceChats( "scripts/female3.voice", &voiceChatLists[2], MAX_VOICECHATS ); + CG_ParseVoiceChats( "scripts/male1.voice", &voiceChatLists[3], MAX_VOICECHATS ); + CG_ParseVoiceChats( "scripts/male2.voice", &voiceChatLists[4], MAX_VOICECHATS ); + CG_ParseVoiceChats( "scripts/male3.voice", &voiceChatLists[5], MAX_VOICECHATS ); + CG_ParseVoiceChats( "scripts/male4.voice", &voiceChatLists[6], MAX_VOICECHATS ); + CG_ParseVoiceChats( "scripts/male5.voice", &voiceChatLists[7], MAX_VOICECHATS ); + CG_Printf("voice chat memory size = %d\n", size - trap_MemoryRemaining()); +} + +/* +================= +CG_HeadModelVoiceChats +================= +*/ +int CG_HeadModelVoiceChats( char *filename ) { + int len, i; + fileHandle_t f; + char buf[MAX_VOICEFILESIZE]; + char **p, *ptr; + char *token; + + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + if ( !f ) { + //trap_Print( va( "voice chat file not found: %s\n", filename ) ); + return -1; + } + if ( len >= MAX_VOICEFILESIZE ) { + trap_Print( va( S_COLOR_RED "voice chat file too large: %s is %i, max allowed is %i", filename, len, MAX_VOICEFILESIZE ) ); + trap_FS_FCloseFile( f ); + return -1; + } + + trap_FS_Read( buf, len, f ); + buf[len] = 0; + trap_FS_FCloseFile( f ); + + ptr = buf; + p = &ptr; + + token = COM_ParseExt(p, qtrue); + if (!token || token[0] == 0) { + return -1; + } + + for ( i = 0; i < MAX_VOICEFILES; i++ ) { + if ( !Q_stricmp(token, voiceChatLists[i].name) ) { + return i; + } + } + + //FIXME: maybe try to load the .voice file which name is stored in token? + + return -1; +} + + +/* +================= +CG_GetVoiceChat +================= +*/ +int CG_GetVoiceChat( voiceChatList_t *voiceChatList, const char *id, sfxHandle_t *snd, char **chat) { + int i, rnd; + + for ( i = 0; i < voiceChatList->numVoiceChats; i++ ) { + if ( !Q_stricmp( id, voiceChatList->voiceChats[i].id ) ) { + rnd = random() * voiceChatList->voiceChats[i].numSounds; + *snd = voiceChatList->voiceChats[i].sounds[rnd]; + *chat = voiceChatList->voiceChats[i].chats[rnd]; + return qtrue; + } + } + return qfalse; +} + +/* +================= +CG_VoiceChatListForClient +================= +*/ +voiceChatList_t *CG_VoiceChatListForClient( int clientNum ) { + clientInfo_t *ci; + int voiceChatNum, i, j, k, gender; + char filename[MAX_QPATH], headModelName[MAX_QPATH]; + + if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { + clientNum = 0; + } + ci = &cgs.clientinfo[ clientNum ]; + + for ( k = 0; k < 2; k++ ) { + if ( k == 0 ) { + if (ci->headModelName[0] == '*') { + Com_sprintf( headModelName, sizeof(headModelName), "%s/%s", ci->headModelName+1, ci->headSkinName ); + } + else { + Com_sprintf( headModelName, sizeof(headModelName), "%s/%s", ci->headModelName, ci->headSkinName ); + } + } + else { + if (ci->headModelName[0] == '*') { + Com_sprintf( headModelName, sizeof(headModelName), "%s", ci->headModelName+1 ); + } + else { + Com_sprintf( headModelName, sizeof(headModelName), "%s", ci->headModelName ); + } + } + // find the voice file for the head model the client uses + for ( i = 0; i < MAX_HEADMODELS; i++ ) { + if (!Q_stricmp(headModelVoiceChat[i].headmodel, headModelName)) { + break; + } + } + if (i < MAX_HEADMODELS) { + return &voiceChatLists[headModelVoiceChat[i].voiceChatNum]; + } + // find a .vc file + for ( i = 0; i < MAX_HEADMODELS; i++ ) { + if (!strlen(headModelVoiceChat[i].headmodel)) { + Com_sprintf(filename, sizeof(filename), "scripts/%s.vc", headModelName); + voiceChatNum = CG_HeadModelVoiceChats(filename); + if (voiceChatNum == -1) + break; + Com_sprintf(headModelVoiceChat[i].headmodel, sizeof ( headModelVoiceChat[i].headmodel ), + "%s", headModelName); + headModelVoiceChat[i].voiceChatNum = voiceChatNum; + return &voiceChatLists[headModelVoiceChat[i].voiceChatNum]; + } + } + } + gender = ci->gender; + for (k = 0; k < 2; k++) { + // just pick the first with the right gender + for ( i = 0; i < MAX_VOICEFILES; i++ ) { + if (strlen(voiceChatLists[i].name)) { + if (voiceChatLists[i].gender == gender) { + // store this head model with voice chat for future reference + for ( j = 0; j < MAX_HEADMODELS; j++ ) { + if (!strlen(headModelVoiceChat[j].headmodel)) { + Com_sprintf(headModelVoiceChat[j].headmodel, sizeof ( headModelVoiceChat[j].headmodel ), + "%s", headModelName); + headModelVoiceChat[j].voiceChatNum = i; + break; + } + } + return &voiceChatLists[i]; + } + } + } + // fall back to male gender because we don't have neuter in the mission pack + if (gender == GENDER_MALE) + break; + gender = GENDER_MALE; + } + // store this head model with voice chat for future reference + for ( j = 0; j < MAX_HEADMODELS; j++ ) { + if (!strlen(headModelVoiceChat[j].headmodel)) { + Com_sprintf(headModelVoiceChat[j].headmodel, sizeof ( headModelVoiceChat[j].headmodel ), + "%s", headModelName); + headModelVoiceChat[j].voiceChatNum = 0; + break; + } + } + // just return the first voice chat list + return &voiceChatLists[0]; +} + +#define MAX_VOICECHATBUFFER 32 + +typedef struct bufferedVoiceChat_s +{ + int clientNum; + sfxHandle_t snd; + int voiceOnly; + char cmd[MAX_SAY_TEXT]; + char message[MAX_SAY_TEXT]; +} bufferedVoiceChat_t; + +bufferedVoiceChat_t voiceChatBuffer[MAX_VOICECHATBUFFER]; + +/* +================= +CG_PlayVoiceChat +================= +*/ +void CG_PlayVoiceChat( bufferedVoiceChat_t *vchat ) { +#ifdef MISSIONPACK + // if we are going into the intermission, don't start any voices + if ( cg.intermissionStarted ) { + return; + } + + if ( !cg_noVoiceChats.integer ) { + trap_S_StartLocalSound( vchat->snd, CHAN_VOICE); + if (vchat->clientNum != cg.snap->ps.clientNum) { + int orderTask = CG_ValidOrder(vchat->cmd); + if (orderTask > 0) { + cgs.acceptOrderTime = cg.time + 5000; + Q_strncpyz(cgs.acceptVoice, vchat->cmd, sizeof(cgs.acceptVoice)); + cgs.acceptTask = orderTask; + cgs.acceptLeader = vchat->clientNum; + } + // see if this was an order + CG_ShowResponseHead(); + } + } + if (!vchat->voiceOnly && !cg_noVoiceText.integer) { + CG_AddToTeamChat( vchat->message ); + CG_Printf( "%s\n", vchat->message ); + } + voiceChatBuffer[cg.voiceChatBufferOut].snd = 0; +#endif +} + +/* +===================== +CG_PlayBufferedVoieChats +===================== +*/ +void CG_PlayBufferedVoiceChats( void ) { +#ifdef MISSIONPACK + if ( cg.voiceChatTime < cg.time ) { + if (cg.voiceChatBufferOut != cg.voiceChatBufferIn && voiceChatBuffer[cg.voiceChatBufferOut].snd) { + // + CG_PlayVoiceChat(&voiceChatBuffer[cg.voiceChatBufferOut]); + // + cg.voiceChatBufferOut = (cg.voiceChatBufferOut + 1) % MAX_VOICECHATBUFFER; + cg.voiceChatTime = cg.time + 1000; + } + } +#endif +} + +/* +===================== +CG_AddBufferedVoiceChat +===================== +*/ +void CG_AddBufferedVoiceChat( bufferedVoiceChat_t *vchat ) { +#ifdef MISSIONPACK + // if we are going into the intermission, don't start any voices + if ( cg.intermissionStarted ) { + return; + } + + memcpy(&voiceChatBuffer[cg.voiceChatBufferIn], vchat, sizeof(bufferedVoiceChat_t)); + cg.voiceChatBufferIn = (cg.voiceChatBufferIn + 1) % MAX_VOICECHATBUFFER; + if (cg.voiceChatBufferIn == cg.voiceChatBufferOut) { + CG_PlayVoiceChat( &voiceChatBuffer[cg.voiceChatBufferOut] ); + cg.voiceChatBufferOut++; + } +#endif +} + +/* +================= +CG_VoiceChatLocal +================= +*/ +void CG_VoiceChatLocal( int mode, qboolean voiceOnly, int clientNum, int color, const char *cmd ) { +#ifdef MISSIONPACK + char *chat; + voiceChatList_t *voiceChatList; + clientInfo_t *ci; + sfxHandle_t snd; + bufferedVoiceChat_t vchat; + + // if we are going into the intermission, don't start any voices + if ( cg.intermissionStarted ) { + return; + } + + if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { + clientNum = 0; + } + ci = &cgs.clientinfo[ clientNum ]; + + cgs.currentVoiceClient = clientNum; + + voiceChatList = CG_VoiceChatListForClient( clientNum ); + + if ( CG_GetVoiceChat( voiceChatList, cmd, &snd, &chat ) ) { + // + if ( mode == SAY_TEAM || !cg_teamChatsOnly.integer ) { + vchat.clientNum = clientNum; + vchat.snd = snd; + vchat.voiceOnly = voiceOnly; + Q_strncpyz(vchat.cmd, cmd, sizeof(vchat.cmd)); + if ( mode == SAY_TELL ) { + Com_sprintf(vchat.message, sizeof(vchat.message), "[%s]: %c%c%s", ci->name, Q_COLOR_ESCAPE, color, chat); + } + else if ( mode == SAY_TEAM ) { + Com_sprintf(vchat.message, sizeof(vchat.message), "(%s): %c%c%s", ci->name, Q_COLOR_ESCAPE, color, chat); + } + else { + Com_sprintf(vchat.message, sizeof(vchat.message), "%s: %c%c%s", ci->name, Q_COLOR_ESCAPE, color, chat); + } + CG_AddBufferedVoiceChat(&vchat); + } + } +#endif +} + +/* +================= +CG_VoiceChat +================= +*/ +void CG_VoiceChat( int mode ) { +#ifdef MISSIONPACK + const char *cmd; + int clientNum, color; + qboolean voiceOnly; + + voiceOnly = atoi(CG_Argv(1)); + clientNum = atoi(CG_Argv(2)); + color = atoi(CG_Argv(3)); + cmd = CG_Argv(4); + + if (cg_noTaunt.integer != 0) { + if (!strcmp(cmd, VOICECHAT_KILLINSULT) || !strcmp(cmd, VOICECHAT_TAUNT) || \ + !strcmp(cmd, VOICECHAT_DEATHINSULT) || !strcmp(cmd, VOICECHAT_KILLGAUNTLET) || \ + !strcmp(cmd, VOICECHAT_PRAISE)) { + return; + } + } + + CG_VoiceChatLocal( mode, voiceOnly, clientNum, color, cmd ); +#endif +} + +/* +================= +CG_RemoveChatEscapeChar +================= +*/ +static void CG_RemoveChatEscapeChar( char *text ) { + int i, l; + + l = 0; + for ( i = 0; text[i]; i++ ) { + if (text[i] == '\x19') + continue; + text[l++] = text[i]; + } + text[l] = '\0'; +} + +/* +================= +CG_ServerCommand + +The string has been tokenized and can be retrieved with +Cmd_Argc() / Cmd_Argv() +================= +*/ +static void CG_ServerCommand( void ) { + const char *cmd; + char text[MAX_SAY_TEXT]; + + cmd = CG_Argv(0); + + if ( !cmd[0] ) { + // server claimed the command + return; + } + + if ( !strcmp( cmd, "cp" ) ) { + CG_CenterPrint( CG_Argv(1), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); + return; + } + + if ( !strcmp( cmd, "cs" ) ) { + CG_ConfigStringModified(); + return; + } + + if ( !strcmp( cmd, "print" ) ) { + CG_Printf( "%s", CG_Argv(1) ); +#ifdef MISSIONPACK + cmd = CG_Argv(1); // yes, this is obviously a hack, but so is the way we hear about + // votes passing or failing + if ( !Q_stricmpn( cmd, "vote failed", 11 ) || !Q_stricmpn( cmd, "team vote failed", 16 )) { + trap_S_StartLocalSound( cgs.media.voteFailed, CHAN_ANNOUNCER ); + } else if ( !Q_stricmpn( cmd, "vote passed", 11 ) || !Q_stricmpn( cmd, "team vote passed", 16 ) ) { + trap_S_StartLocalSound( cgs.media.votePassed, CHAN_ANNOUNCER ); + } +#endif + return; + } + + if ( !strcmp( cmd, "chat" ) ) { + if ( !cg_teamChatsOnly.integer ) { + trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); + Q_strncpyz( text, CG_Argv(1), MAX_SAY_TEXT ); + CG_RemoveChatEscapeChar( text ); + CG_Printf( "%s\n", text ); + } + return; + } + + if ( !strcmp( cmd, "tchat" ) ) { + trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); + Q_strncpyz( text, CG_Argv(1), MAX_SAY_TEXT ); + CG_RemoveChatEscapeChar( text ); + CG_AddToTeamChat( text ); + CG_Printf( "%s\n", text ); + return; + } + if ( !strcmp( cmd, "vchat" ) ) { + CG_VoiceChat( SAY_ALL ); + return; + } + + if ( !strcmp( cmd, "vtchat" ) ) { + CG_VoiceChat( SAY_TEAM ); + return; + } + + if ( !strcmp( cmd, "vtell" ) ) { + CG_VoiceChat( SAY_TELL ); + return; + } + + if ( !strcmp( cmd, "scores" ) ) { + CG_ParseScores(); + return; + } + + if ( !strcmp( cmd, "tinfo" ) ) { + CG_ParseTeamInfo(); + return; + } + + if ( !strcmp( cmd, "map_restart" ) ) { + CG_MapRestart(); + return; + } + + if ( Q_stricmp (cmd, "remapShader") == 0 ) + { + if (trap_Argc() == 4) + { + char shader1[MAX_QPATH]; + char shader2[MAX_QPATH]; + char shader3[MAX_QPATH]; + + Q_strncpyz(shader1, CG_Argv(1), sizeof(shader1)); + Q_strncpyz(shader2, CG_Argv(2), sizeof(shader2)); + Q_strncpyz(shader3, CG_Argv(3), sizeof(shader3)); + + trap_R_RemapShader(shader1, shader2, shader3); + } + + return; + } + + // loaddeferred can be both a servercmd and a consolecmd + if ( !strcmp( cmd, "loaddefered" ) ) { // FIXME: spelled wrong, but not changing for demo + CG_LoadDeferredPlayers(); + return; + } + + // clientLevelShot is sent before taking a special screenshot for + // the menu system during development + if ( !strcmp( cmd, "clientLevelShot" ) ) { + cg.levelShot = qtrue; + return; + } + + CG_Printf( "Unknown client game command: %s\n", cmd ); +} + + +/* +==================== +CG_ExecuteNewServerCommands + +Execute all of the server commands that were received along +with this this snapshot. +==================== +*/ +void CG_ExecuteNewServerCommands( int latestSequence ) { + while ( cgs.serverCommandSequence < latestSequence ) { + if ( trap_GetServerCommand( ++cgs.serverCommandSequence ) ) { + CG_ServerCommand(); + } + } +} diff --git a/reaction/engine/code/cgame/cg_snapshot.c b/reaction/engine/code/cgame/cg_snapshot.c new file mode 100644 index 00000000..07b005ec --- /dev/null +++ b/reaction/engine/code/cgame/cg_snapshot.c @@ -0,0 +1,403 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_snapshot.c -- things that happen on snapshot transition, +// not necessarily every single rendered frame + +#include "cg_local.h" + + + +/* +================== +CG_ResetEntity +================== +*/ +static void CG_ResetEntity( centity_t *cent ) { + // if the previous snapshot this entity was updated in is at least + // an event window back in time then we can reset the previous event + if ( cent->snapShotTime < cg.time - EVENT_VALID_MSEC ) { + cent->previousEvent = 0; + } + + cent->trailTime = cg.snap->serverTime; + + VectorCopy (cent->currentState.origin, cent->lerpOrigin); + VectorCopy (cent->currentState.angles, cent->lerpAngles); + if ( cent->currentState.eType == ET_PLAYER ) { + CG_ResetPlayerEntity( cent ); + } +} + +/* +=============== +CG_TransitionEntity + +cent->nextState is moved to cent->currentState and events are fired +=============== +*/ +static void CG_TransitionEntity( centity_t *cent ) { + cent->currentState = cent->nextState; + cent->currentValid = qtrue; + + // reset if the entity wasn't in the last frame or was teleported + if ( !cent->interpolate ) { + CG_ResetEntity( cent ); + } + + // clear the next state. if will be set by the next CG_SetNextSnap + cent->interpolate = qfalse; + + // check for events + CG_CheckEvents( cent ); +} + + +/* +================== +CG_SetInitialSnapshot + +This will only happen on the very first snapshot, or +on tourney restarts. All other times will use +CG_TransitionSnapshot instead. + +FIXME: Also called by map_restart? +================== +*/ +void CG_SetInitialSnapshot( snapshot_t *snap ) { + int i; + centity_t *cent; + entityState_t *state; + + cg.snap = snap; + + BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].currentState, qfalse ); + + // sort out solid entities + CG_BuildSolidList(); + + CG_ExecuteNewServerCommands( snap->serverCommandSequence ); + + // set our local weapon selection pointer to + // what the server has indicated the current weapon is + CG_Respawn(); + + for ( i = 0 ; i < cg.snap->numEntities ; i++ ) { + state = &cg.snap->entities[ i ]; + cent = &cg_entities[ state->number ]; + + memcpy(¢->currentState, state, sizeof(entityState_t)); + //cent->currentState = *state; + cent->interpolate = qfalse; + cent->currentValid = qtrue; + + CG_ResetEntity( cent ); + + // check for events + CG_CheckEvents( cent ); + } +} + + +/* +=================== +CG_TransitionSnapshot + +The transition point from snap to nextSnap has passed +=================== +*/ +static void CG_TransitionSnapshot( void ) { + centity_t *cent; + snapshot_t *oldFrame; + int i; + + if ( !cg.snap ) { + CG_Error( "CG_TransitionSnapshot: NULL cg.snap" ); + } + if ( !cg.nextSnap ) { + CG_Error( "CG_TransitionSnapshot: NULL cg.nextSnap" ); + } + + // execute any server string commands before transitioning entities + CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence ); + + // if we had a map_restart, set everthing with initial + if ( !cg.snap ) { + } + + // clear the currentValid flag for all entities in the existing snapshot + for ( i = 0 ; i < cg.snap->numEntities ; i++ ) { + cent = &cg_entities[ cg.snap->entities[ i ].number ]; + cent->currentValid = qfalse; + } + + // move nextSnap to snap and do the transitions + oldFrame = cg.snap; + cg.snap = cg.nextSnap; + + BG_PlayerStateToEntityState( &cg.snap->ps, &cg_entities[ cg.snap->ps.clientNum ].currentState, qfalse ); + cg_entities[ cg.snap->ps.clientNum ].interpolate = qfalse; + + for ( i = 0 ; i < cg.snap->numEntities ; i++ ) { + cent = &cg_entities[ cg.snap->entities[ i ].number ]; + CG_TransitionEntity( cent ); + + // remember time of snapshot this entity was last updated in + cent->snapShotTime = cg.snap->serverTime; + } + + cg.nextSnap = NULL; + + // check for playerstate transition events + if ( oldFrame ) { + playerState_t *ops, *ps; + + ops = &oldFrame->ps; + ps = &cg.snap->ps; + // teleporting checks are irrespective of prediction + if ( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT ) { + cg.thisFrameTeleport = qtrue; // will be cleared by prediction code + } + + // if we are not doing client side movement prediction for any + // reason, then the client events and view changes will be issued now + if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) + || cg_nopredict.integer || cg_synchronousClients.integer ) { + CG_TransitionPlayerState( ps, ops ); + } + } + +} + + +/* +=================== +CG_SetNextSnap + +A new snapshot has just been read in from the client system. +=================== +*/ +static void CG_SetNextSnap( snapshot_t *snap ) { + int num; + entityState_t *es; + centity_t *cent; + + cg.nextSnap = snap; + + BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].nextState, qfalse ); + cg_entities[ cg.snap->ps.clientNum ].interpolate = qtrue; + + // check for extrapolation errors + for ( num = 0 ; num < snap->numEntities ; num++ ) { + es = &snap->entities[num]; + cent = &cg_entities[ es->number ]; + + memcpy(¢->nextState, es, sizeof(entityState_t)); + //cent->nextState = *es; + + // if this frame is a teleport, or the entity wasn't in the + // previous frame, don't interpolate + if ( !cent->currentValid || ( ( cent->currentState.eFlags ^ es->eFlags ) & EF_TELEPORT_BIT ) ) { + cent->interpolate = qfalse; + } else { + cent->interpolate = qtrue; + } + } + + // if the next frame is a teleport for the playerstate, we + // can't interpolate during demos + if ( cg.snap && ( ( snap->ps.eFlags ^ cg.snap->ps.eFlags ) & EF_TELEPORT_BIT ) ) { + cg.nextFrameTeleport = qtrue; + } else { + cg.nextFrameTeleport = qfalse; + } + + // if changing follow mode, don't interpolate + if ( cg.nextSnap->ps.clientNum != cg.snap->ps.clientNum ) { + cg.nextFrameTeleport = qtrue; + } + + // if changing server restarts, don't interpolate + if ( ( cg.nextSnap->snapFlags ^ cg.snap->snapFlags ) & SNAPFLAG_SERVERCOUNT ) { + cg.nextFrameTeleport = qtrue; + } + + // sort out solid entities + CG_BuildSolidList(); +} + + +/* +======================== +CG_ReadNextSnapshot + +This is the only place new snapshots are requested +This may increment cgs.processedSnapshotNum multiple +times if the client system fails to return a +valid snapshot. +======================== +*/ +static snapshot_t *CG_ReadNextSnapshot( void ) { + qboolean r; + snapshot_t *dest; + + if ( cg.latestSnapshotNum > cgs.processedSnapshotNum + 1000 ) { + CG_Printf( "WARNING: CG_ReadNextSnapshot: way out of range, %i > %i", + cg.latestSnapshotNum, cgs.processedSnapshotNum ); + } + + while ( cgs.processedSnapshotNum < cg.latestSnapshotNum ) { + // decide which of the two slots to load it into + if ( cg.snap == &cg.activeSnapshots[0] ) { + dest = &cg.activeSnapshots[1]; + } else { + dest = &cg.activeSnapshots[0]; + } + + // try to read the snapshot from the client system + cgs.processedSnapshotNum++; + r = trap_GetSnapshot( cgs.processedSnapshotNum, dest ); + + // FIXME: why would trap_GetSnapshot return a snapshot with the same server time + if ( cg.snap && r && dest->serverTime == cg.snap->serverTime ) { + //continue; + } + + // if it succeeded, return + if ( r ) { + CG_AddLagometerSnapshotInfo( dest ); + return dest; + } + + // a GetSnapshot will return failure if the snapshot + // never arrived, or is so old that its entities + // have been shoved off the end of the circular + // buffer in the client system. + + // record as a dropped packet + CG_AddLagometerSnapshotInfo( NULL ); + + // If there are additional snapshots, continue trying to + // read them. + } + + // nothing left to read + return NULL; +} + + +/* +============ +CG_ProcessSnapshots + +We are trying to set up a renderable view, so determine +what the simulated time is, and try to get snapshots +both before and after that time if available. + +If we don't have a valid cg.snap after exiting this function, +then a 3D game view cannot be rendered. This should only happen +right after the initial connection. After cg.snap has been valid +once, it will never turn invalid. + +Even if cg.snap is valid, cg.nextSnap may not be, if the snapshot +hasn't arrived yet (it becomes an extrapolating situation instead +of an interpolating one) + +============ +*/ +void CG_ProcessSnapshots( void ) { + snapshot_t *snap; + int n; + + // see what the latest snapshot the client system has is + trap_GetCurrentSnapshotNumber( &n, &cg.latestSnapshotTime ); + if ( n != cg.latestSnapshotNum ) { + if ( n < cg.latestSnapshotNum ) { + // this should never happen + CG_Error( "CG_ProcessSnapshots: n < cg.latestSnapshotNum" ); + } + cg.latestSnapshotNum = n; + } + + // If we have yet to receive a snapshot, check for it. + // Once we have gotten the first snapshot, cg.snap will + // always have valid data for the rest of the game + while ( !cg.snap ) { + snap = CG_ReadNextSnapshot(); + if ( !snap ) { + // we can't continue until we get a snapshot + return; + } + + // set our weapon selection to what + // the playerstate is currently using + if ( !( snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) { + CG_SetInitialSnapshot( snap ); + } + } + + // loop until we either have a valid nextSnap with a serverTime + // greater than cg.time to interpolate towards, or we run + // out of available snapshots + do { + // if we don't have a nextframe, try and read a new one in + if ( !cg.nextSnap ) { + snap = CG_ReadNextSnapshot(); + + // if we still don't have a nextframe, we will just have to + // extrapolate + if ( !snap ) { + break; + } + + CG_SetNextSnap( snap ); + + + // if time went backwards, we have a level restart + if ( cg.nextSnap->serverTime < cg.snap->serverTime ) { + CG_Error( "CG_ProcessSnapshots: Server time went backwards" ); + } + } + + // if our time is < nextFrame's, we have a nice interpolating state + if ( cg.time >= cg.snap->serverTime && cg.time < cg.nextSnap->serverTime ) { + break; + } + + // we have passed the transition from nextFrame to frame + CG_TransitionSnapshot(); + } while ( 1 ); + + // assert our valid conditions upon exiting + if ( cg.snap == NULL ) { + CG_Error( "CG_ProcessSnapshots: cg.snap == NULL" ); + } + if ( cg.time < cg.snap->serverTime ) { + // this can happen right after a vid_restart + cg.time = cg.snap->serverTime; + } + if ( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time ) { + CG_Error( "CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time" ); + } + +} + diff --git a/reaction/engine/code/cgame/cg_syscalls.asm b/reaction/engine/code/cgame/cg_syscalls.asm new file mode 100644 index 00000000..0874b479 --- /dev/null +++ b/reaction/engine/code/cgame/cg_syscalls.asm @@ -0,0 +1,106 @@ +code + +equ trap_Print -1 +equ trap_Error -2 +equ trap_Milliseconds -3 +equ trap_Cvar_Register -4 +equ trap_Cvar_Update -5 +equ trap_Cvar_Set -6 +equ trap_Cvar_VariableStringBuffer -7 +equ trap_Argc -8 +equ trap_Argv -9 +equ trap_Args -10 +equ trap_FS_FOpenFile -11 +equ trap_FS_Read -12 +equ trap_FS_Write -13 +equ trap_FS_FCloseFile -14 +equ trap_SendConsoleCommand -15 +equ trap_AddCommand -16 +equ trap_SendClientCommand -17 +equ trap_UpdateScreen -18 +equ trap_CM_LoadMap -19 +equ trap_CM_NumInlineModels -20 +equ trap_CM_InlineModel -21 +equ trap_CM_LoadModel -22 +equ trap_CM_TempBoxModel -23 +equ trap_CM_PointContents -24 +equ trap_CM_TransformedPointContents -25 +equ trap_CM_BoxTrace -26 +equ trap_CM_TransformedBoxTrace -27 +equ trap_CM_MarkFragments -28 +equ trap_S_StartSound -29 +equ trap_S_StartLocalSound -30 +equ trap_S_ClearLoopingSounds -31 +equ trap_S_AddLoopingSound -32 +equ trap_S_UpdateEntityPosition -33 +equ trap_S_Respatialize -34 +equ trap_S_RegisterSound -35 +equ trap_S_StartBackgroundTrack -36 +equ trap_R_LoadWorldMap -37 +equ trap_R_RegisterModel -38 +equ trap_R_RegisterSkin -39 +equ trap_R_RegisterShader -40 +equ trap_R_ClearScene -41 +equ trap_R_AddRefEntityToScene -42 +equ trap_R_AddPolyToScene -43 +equ trap_R_AddLightToScene -44 +equ trap_R_RenderScene -45 +equ trap_R_SetColor -46 +equ trap_R_DrawStretchPic -47 +equ trap_R_ModelBounds -48 +equ trap_R_LerpTag -49 +equ trap_GetGlconfig -50 +equ trap_GetGameState -51 +equ trap_GetCurrentSnapshotNumber -52 +equ trap_GetSnapshot -53 +equ trap_GetServerCommand -54 +equ trap_GetCurrentCmdNumber -55 +equ trap_GetUserCmd -56 +equ trap_SetUserCmdValue -57 +equ trap_R_RegisterShaderNoMip -58 +equ trap_MemoryRemaining -59 +equ trap_R_RegisterFont -60 +equ trap_Key_IsDown -61 +equ trap_Key_GetCatcher -62 +equ trap_Key_SetCatcher -63 +equ trap_Key_GetKey -64 +equ trap_PC_AddGlobalDefine -65 +equ trap_PC_LoadSource -66 +equ trap_PC_FreeSource -67 +equ trap_PC_ReadToken -68 +equ trap_PC_SourceFileAndLine -69 +equ trap_S_StopBackgroundTrack -70 +equ trap_RealTime -71 +equ trap_SnapVector -72 +equ trap_RemoveCommand -73 +equ trap_R_LightForPoint -74 +equ trap_CIN_PlayCinematic -75 +equ trap_CIN_StopCinematic -76 +equ trap_CIN_RunCinematic -77 +equ trap_CIN_DrawCinematic -78 +equ trap_CIN_SetExtents -79 +equ trap_R_RemapShader -80 +equ trap_S_AddRealLoopingSound -81 +equ trap_S_StopLoopingSound -82 +equ trap_CM_TempCapsuleModel -83 +equ trap_CM_CapsuleTrace -84 +equ trap_CM_TransformedCapsuleTrace -85 +equ trap_R_AddAdditiveLightToScene -86 +equ trap_GetEntityToken -87 +equ trap_R_AddPolysToScene -88 +equ trap_R_inPVS -89 +equ trap_FS_Seek -90 + +equ memset -101 +equ memcpy -102 +equ strncpy -103 +equ sin -104 +equ cos -105 +equ atan2 -106 +equ sqrt -107 +equ floor -108 +equ ceil -109 +equ testPrintInt -110 +equ testPrintFloat -111 +equ acos -112 + diff --git a/reaction/engine/code/cgame/cg_syscalls.c b/reaction/engine/code/cgame/cg_syscalls.c new file mode 100644 index 00000000..cdc10601 --- /dev/null +++ b/reaction/engine/code/cgame/cg_syscalls.c @@ -0,0 +1,445 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_syscalls.c -- this file is only included when building a dll +// cg_syscalls.asm is included instead when building a qvm +#ifdef Q3_VM +#error "Do not use in VM build" +#endif + +#include "cg_local.h" + +static intptr_t (QDECL *syscall)( intptr_t arg, ... ) = (intptr_t (QDECL *)( intptr_t, ...))-1; + + +void dllEntry( intptr_t (QDECL *syscallptr)( intptr_t arg,... ) ) { + syscall = syscallptr; +} + + +int PASSFLOAT( float x ) { + floatint_t fi; + fi.f = x; + return fi.i; +} + +void trap_Print( const char *fmt ) { + syscall( CG_PRINT, fmt ); +} + +void trap_Error( const char *fmt ) { + syscall( CG_ERROR, fmt ); +} + +int trap_Milliseconds( void ) { + return syscall( CG_MILLISECONDS ); +} + +void trap_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ) { + syscall( CG_CVAR_REGISTER, vmCvar, varName, defaultValue, flags ); +} + +void trap_Cvar_Update( vmCvar_t *vmCvar ) { + syscall( CG_CVAR_UPDATE, vmCvar ); +} + +void trap_Cvar_Set( const char *var_name, const char *value ) { + syscall( CG_CVAR_SET, var_name, value ); +} + +void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ) { + syscall( CG_CVAR_VARIABLESTRINGBUFFER, var_name, buffer, bufsize ); +} + +int trap_Argc( void ) { + return syscall( CG_ARGC ); +} + +void trap_Argv( int n, char *buffer, int bufferLength ) { + syscall( CG_ARGV, n, buffer, bufferLength ); +} + +void trap_Args( char *buffer, int bufferLength ) { + syscall( CG_ARGS, buffer, bufferLength ); +} + +int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ) { + return syscall( CG_FS_FOPENFILE, qpath, f, mode ); +} + +void trap_FS_Read( void *buffer, int len, fileHandle_t f ) { + syscall( CG_FS_READ, buffer, len, f ); +} + +void trap_FS_Write( const void *buffer, int len, fileHandle_t f ) { + syscall( CG_FS_WRITE, buffer, len, f ); +} + +void trap_FS_FCloseFile( fileHandle_t f ) { + syscall( CG_FS_FCLOSEFILE, f ); +} + +int trap_FS_Seek( fileHandle_t f, long offset, int origin ) { + return syscall( CG_FS_SEEK, f, offset, origin ); +} + +void trap_SendConsoleCommand( const char *text ) { + syscall( CG_SENDCONSOLECOMMAND, text ); +} + +void trap_AddCommand( const char *cmdName ) { + syscall( CG_ADDCOMMAND, cmdName ); +} + +void trap_RemoveCommand( const char *cmdName ) { + syscall( CG_REMOVECOMMAND, cmdName ); +} + +void trap_SendClientCommand( const char *s ) { + syscall( CG_SENDCLIENTCOMMAND, s ); +} + +void trap_UpdateScreen( void ) { + syscall( CG_UPDATESCREEN ); +} + +void trap_CM_LoadMap( const char *mapname ) { + syscall( CG_CM_LOADMAP, mapname ); +} + +int trap_CM_NumInlineModels( void ) { + return syscall( CG_CM_NUMINLINEMODELS ); +} + +clipHandle_t trap_CM_InlineModel( int index ) { + return syscall( CG_CM_INLINEMODEL, index ); +} + +clipHandle_t trap_CM_TempBoxModel( const vec3_t mins, const vec3_t maxs ) { + return syscall( CG_CM_TEMPBOXMODEL, mins, maxs ); +} + +clipHandle_t trap_CM_TempCapsuleModel( const vec3_t mins, const vec3_t maxs ) { + return syscall( CG_CM_TEMPCAPSULEMODEL, mins, maxs ); +} + +int trap_CM_PointContents( const vec3_t p, clipHandle_t model ) { + return syscall( CG_CM_POINTCONTENTS, p, model ); +} + +int trap_CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles ) { + return syscall( CG_CM_TRANSFORMEDPOINTCONTENTS, p, model, origin, angles ); +} + +void trap_CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end, + const vec3_t mins, const vec3_t maxs, + clipHandle_t model, int brushmask ) { + syscall( CG_CM_BOXTRACE, results, start, end, mins, maxs, model, brushmask ); +} + +void trap_CM_CapsuleTrace( trace_t *results, const vec3_t start, const vec3_t end, + const vec3_t mins, const vec3_t maxs, + clipHandle_t model, int brushmask ) { + syscall( CG_CM_CAPSULETRACE, results, start, end, mins, maxs, model, brushmask ); +} + +void trap_CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end, + const vec3_t mins, const vec3_t maxs, + clipHandle_t model, int brushmask, + const vec3_t origin, const vec3_t angles ) { + syscall( CG_CM_TRANSFORMEDBOXTRACE, results, start, end, mins, maxs, model, brushmask, origin, angles ); +} + +void trap_CM_TransformedCapsuleTrace( trace_t *results, const vec3_t start, const vec3_t end, + const vec3_t mins, const vec3_t maxs, + clipHandle_t model, int brushmask, + const vec3_t origin, const vec3_t angles ) { + syscall( CG_CM_TRANSFORMEDCAPSULETRACE, results, start, end, mins, maxs, model, brushmask, origin, angles ); +} + +int trap_CM_MarkFragments( int numPoints, const vec3_t *points, + const vec3_t projection, + int maxPoints, vec3_t pointBuffer, + int maxFragments, markFragment_t *fragmentBuffer ) { + return syscall( CG_CM_MARKFRAGMENTS, numPoints, points, projection, maxPoints, pointBuffer, maxFragments, fragmentBuffer ); +} + +void trap_S_StartSound( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx ) { + syscall( CG_S_STARTSOUND, origin, entityNum, entchannel, sfx ); +} + +void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum ) { + syscall( CG_S_STARTLOCALSOUND, sfx, channelNum ); +} + +void trap_S_ClearLoopingSounds( qboolean killall ) { + syscall( CG_S_CLEARLOOPINGSOUNDS, killall ); +} + +void trap_S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) { + syscall( CG_S_ADDLOOPINGSOUND, entityNum, origin, velocity, sfx ); +} + +void trap_S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) { + syscall( CG_S_ADDREALLOOPINGSOUND, entityNum, origin, velocity, sfx ); +} + +void trap_S_StopLoopingSound( int entityNum ) { + syscall( CG_S_STOPLOOPINGSOUND, entityNum ); +} + +void trap_S_UpdateEntityPosition( int entityNum, const vec3_t origin ) { + syscall( CG_S_UPDATEENTITYPOSITION, entityNum, origin ); +} + +void trap_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ) { + syscall( CG_S_RESPATIALIZE, entityNum, origin, axis, inwater ); +} + +sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed ) { + return syscall( CG_S_REGISTERSOUND, sample, compressed ); +} + +void trap_S_StartBackgroundTrack( const char *intro, const char *loop ) { + syscall( CG_S_STARTBACKGROUNDTRACK, intro, loop ); +} + +void trap_R_LoadWorldMap( const char *mapname ) { + syscall( CG_R_LOADWORLDMAP, mapname ); +} + +qhandle_t trap_R_RegisterModel( const char *name ) { + return syscall( CG_R_REGISTERMODEL, name ); +} + +qhandle_t trap_R_RegisterSkin( const char *name ) { + return syscall( CG_R_REGISTERSKIN, name ); +} + +qhandle_t trap_R_RegisterShader( const char *name ) { + return syscall( CG_R_REGISTERSHADER, name ); +} + +qhandle_t trap_R_RegisterShaderNoMip( const char *name ) { + return syscall( CG_R_REGISTERSHADERNOMIP, name ); +} + +void trap_R_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font) { + syscall(CG_R_REGISTERFONT, fontName, pointSize, font ); +} + +void trap_R_ClearScene( void ) { + syscall( CG_R_CLEARSCENE ); +} + +void trap_R_AddRefEntityToScene( const refEntity_t *re ) { + syscall( CG_R_ADDREFENTITYTOSCENE, re ); +} + +void trap_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts ) { + syscall( CG_R_ADDPOLYTOSCENE, hShader, numVerts, verts ); +} + +void trap_R_AddPolysToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num ) { + syscall( CG_R_ADDPOLYSTOSCENE, hShader, numVerts, verts, num ); +} + +int trap_R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ) { + return syscall( CG_R_LIGHTFORPOINT, point, ambientLight, directedLight, lightDir ); +} + +void trap_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ) { + syscall( CG_R_ADDLIGHTTOSCENE, org, PASSFLOAT(intensity), PASSFLOAT(r), PASSFLOAT(g), PASSFLOAT(b) ); +} + +void trap_R_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b ) { + syscall( CG_R_ADDADDITIVELIGHTTOSCENE, org, PASSFLOAT(intensity), PASSFLOAT(r), PASSFLOAT(g), PASSFLOAT(b) ); +} + +void trap_R_RenderScene( const refdef_t *fd ) { + syscall( CG_R_RENDERSCENE, fd ); +} + +void trap_R_SetColor( const float *rgba ) { + syscall( CG_R_SETCOLOR, rgba ); +} + +void trap_R_DrawStretchPic( float x, float y, float w, float h, + float s1, float t1, float s2, float t2, qhandle_t hShader ) { + syscall( CG_R_DRAWSTRETCHPIC, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h), PASSFLOAT(s1), PASSFLOAT(t1), PASSFLOAT(s2), PASSFLOAT(t2), hShader ); +} + +void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ) { + syscall( CG_R_MODELBOUNDS, model, mins, maxs ); +} + +int trap_R_LerpTag( orientation_t *tag, clipHandle_t mod, int startFrame, int endFrame, + float frac, const char *tagName ) { + return syscall( CG_R_LERPTAG, tag, mod, startFrame, endFrame, PASSFLOAT(frac), tagName ); +} + +void trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ) { + syscall( CG_R_REMAP_SHADER, oldShader, newShader, timeOffset ); +} + +void trap_GetGlconfig( glconfig_t *glconfig ) { + syscall( CG_GETGLCONFIG, glconfig ); +} + +void trap_GetGameState( gameState_t *gamestate ) { + syscall( CG_GETGAMESTATE, gamestate ); +} + +void trap_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ) { + syscall( CG_GETCURRENTSNAPSHOTNUMBER, snapshotNumber, serverTime ); +} + +qboolean trap_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) { + return syscall( CG_GETSNAPSHOT, snapshotNumber, snapshot ); +} + +qboolean trap_GetServerCommand( int serverCommandNumber ) { + return syscall( CG_GETSERVERCOMMAND, serverCommandNumber ); +} + +int trap_GetCurrentCmdNumber( void ) { + return syscall( CG_GETCURRENTCMDNUMBER ); +} + +qboolean trap_GetUserCmd( int cmdNumber, usercmd_t *ucmd ) { + return syscall( CG_GETUSERCMD, cmdNumber, ucmd ); +} + +void trap_SetUserCmdValue( int stateValue, float sensitivityScale ) { + syscall( CG_SETUSERCMDVALUE, stateValue, PASSFLOAT(sensitivityScale) ); +} + +void testPrintInt( char *string, int i ) { + syscall( CG_TESTPRINTINT, string, i ); +} + +void testPrintFloat( char *string, float f ) { + syscall( CG_TESTPRINTFLOAT, string, PASSFLOAT(f) ); +} + +int trap_MemoryRemaining( void ) { + return syscall( CG_MEMORY_REMAINING ); +} + +qboolean trap_Key_IsDown( int keynum ) { + return syscall( CG_KEY_ISDOWN, keynum ); +} + +int trap_Key_GetCatcher( void ) { + return syscall( CG_KEY_GETCATCHER ); +} + +void trap_Key_SetCatcher( int catcher ) { + syscall( CG_KEY_SETCATCHER, catcher ); +} + +int trap_Key_GetKey( const char *binding ) { + return syscall( CG_KEY_GETKEY, binding ); +} + +int trap_PC_AddGlobalDefine( char *define ) { + return syscall( CG_PC_ADD_GLOBAL_DEFINE, define ); +} + +int trap_PC_LoadSource( const char *filename ) { + return syscall( CG_PC_LOAD_SOURCE, filename ); +} + +int trap_PC_FreeSource( int handle ) { + return syscall( CG_PC_FREE_SOURCE, handle ); +} + +int trap_PC_ReadToken( int handle, pc_token_t *pc_token ) { + return syscall( CG_PC_READ_TOKEN, handle, pc_token ); +} + +int trap_PC_SourceFileAndLine( int handle, char *filename, int *line ) { + return syscall( CG_PC_SOURCE_FILE_AND_LINE, handle, filename, line ); +} + +void trap_S_StopBackgroundTrack( void ) { + syscall( CG_S_STOPBACKGROUNDTRACK ); +} + +int trap_RealTime(qtime_t *qtime) { + return syscall( CG_REAL_TIME, qtime ); +} + +void trap_SnapVector( float *v ) { + syscall( CG_SNAPVECTOR, v ); +} + +// this returns a handle. arg0 is the name in the format "idlogo.roq", set arg1 to NULL, alteredstates to qfalse (do not alter gamestate) +int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits) { + return syscall(CG_CIN_PLAYCINEMATIC, arg0, xpos, ypos, width, height, bits); +} + +// stops playing the cinematic and ends it. should always return FMV_EOF +// cinematics must be stopped in reverse order of when they are started +e_status trap_CIN_StopCinematic(int handle) { + return syscall(CG_CIN_STOPCINEMATIC, handle); +} + + +// will run a frame of the cinematic but will not draw it. Will return FMV_EOF if the end of the cinematic has been reached. +e_status trap_CIN_RunCinematic (int handle) { + return syscall(CG_CIN_RUNCINEMATIC, handle); +} + + +// draws the current frame +void trap_CIN_DrawCinematic (int handle) { + syscall(CG_CIN_DRAWCINEMATIC, handle); +} + + +// allows you to resize the animation dynamically +void trap_CIN_SetExtents (int handle, int x, int y, int w, int h) { + syscall(CG_CIN_SETEXTENTS, handle, x, y, w, h); +} + +/* +qboolean trap_loadCamera( const char *name ) { + return syscall( CG_LOADCAMERA, name ); +} + +void trap_startCamera(int time) { + syscall(CG_STARTCAMERA, time); +} + +qboolean trap_getCameraInfo( int time, vec3_t *origin, vec3_t *angles) { + return syscall( CG_GETCAMERAINFO, time, origin, angles ); +} +*/ + +qboolean trap_GetEntityToken( char *buffer, int bufferSize ) { + return syscall( CG_GET_ENTITY_TOKEN, buffer, bufferSize ); +} + +qboolean trap_R_inPVS( const vec3_t p1, const vec3_t p2 ) { + return syscall( CG_R_INPVS, p1, p2 ); +} diff --git a/reaction/engine/code/cgame/cg_view.c b/reaction/engine/code/cgame/cg_view.c new file mode 100644 index 00000000..f61bbf90 --- /dev/null +++ b/reaction/engine/code/cgame/cg_view.c @@ -0,0 +1,876 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_view.c -- setup all the parameters (position, angle, etc) +// for a 3D rendering +#include "cg_local.h" + + +/* +============================================================================= + + MODEL TESTING + +The viewthing and gun positioning tools from Q2 have been integrated and +enhanced into a single model testing facility. + +Model viewing can begin with either "testmodel " or "testgun ". + +The names must be the full pathname after the basedir, like +"models/weapons/v_launch/tris.md3" or "players/male/tris.md3" + +Testmodel will create a fake entity 100 units in front of the current view +position, directly facing the viewer. It will remain immobile, so you can +move around it to view it from different angles. + +Testgun will cause the model to follow the player around and supress the real +view weapon model. The default frame 0 of most guns is completely off screen, +so you will probably have to cycle a couple frames to see it. + +"nextframe", "prevframe", "nextskin", and "prevskin" commands will change the +frame or skin of the testmodel. These are bound to F5, F6, F7, and F8 in +q3default.cfg. + +If a gun is being tested, the "gun_x", "gun_y", and "gun_z" variables will let +you adjust the positioning. + +Note that none of the model testing features update while the game is paused, so +it may be convenient to test with deathmatch set to 1 so that bringing down the +console doesn't pause the game. + +============================================================================= +*/ + +/* +================= +CG_TestModel_f + +Creates an entity in front of the current position, which +can then be moved around +================= +*/ +void CG_TestModel_f (void) { + vec3_t angles; + + memset( &cg.testModelEntity, 0, sizeof(cg.testModelEntity) ); + if ( trap_Argc() < 2 ) { + return; + } + + Q_strncpyz (cg.testModelName, CG_Argv( 1 ), MAX_QPATH ); + cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName ); + + if ( trap_Argc() == 3 ) { + cg.testModelEntity.backlerp = atof( CG_Argv( 2 ) ); + cg.testModelEntity.frame = 1; + cg.testModelEntity.oldframe = 0; + } + if (! cg.testModelEntity.hModel ) { + CG_Printf( "Can't register model\n" ); + return; + } + + VectorMA( cg.refdef.vieworg, 100, cg.refdef.viewaxis[0], cg.testModelEntity.origin ); + + angles[PITCH] = 0; + angles[YAW] = 180 + cg.refdefViewAngles[1]; + angles[ROLL] = 0; + + AnglesToAxis( angles, cg.testModelEntity.axis ); + cg.testGun = qfalse; +} + +/* +================= +CG_TestGun_f + +Replaces the current view weapon with the given model +================= +*/ +void CG_TestGun_f (void) { + CG_TestModel_f(); + cg.testGun = qtrue; + cg.testModelEntity.renderfx = RF_MINLIGHT | RF_DEPTHHACK | RF_FIRST_PERSON; +} + + +void CG_TestModelNextFrame_f (void) { + cg.testModelEntity.frame++; + CG_Printf( "frame %i\n", cg.testModelEntity.frame ); +} + +void CG_TestModelPrevFrame_f (void) { + cg.testModelEntity.frame--; + if ( cg.testModelEntity.frame < 0 ) { + cg.testModelEntity.frame = 0; + } + CG_Printf( "frame %i\n", cg.testModelEntity.frame ); +} + +void CG_TestModelNextSkin_f (void) { + cg.testModelEntity.skinNum++; + CG_Printf( "skin %i\n", cg.testModelEntity.skinNum ); +} + +void CG_TestModelPrevSkin_f (void) { + cg.testModelEntity.skinNum--; + if ( cg.testModelEntity.skinNum < 0 ) { + cg.testModelEntity.skinNum = 0; + } + CG_Printf( "skin %i\n", cg.testModelEntity.skinNum ); +} + +static void CG_AddTestModel (void) { + int i; + + // re-register the model, because the level may have changed + cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName ); + if (! cg.testModelEntity.hModel ) { + CG_Printf ("Can't register model\n"); + return; + } + + // if testing a gun, set the origin reletive to the view origin + if ( cg.testGun ) { + VectorCopy( cg.refdef.vieworg, cg.testModelEntity.origin ); + VectorCopy( cg.refdef.viewaxis[0], cg.testModelEntity.axis[0] ); + VectorCopy( cg.refdef.viewaxis[1], cg.testModelEntity.axis[1] ); + VectorCopy( cg.refdef.viewaxis[2], cg.testModelEntity.axis[2] ); + + // allow the position to be adjusted + for (i=0 ; i<3 ; i++) { + cg.testModelEntity.origin[i] += cg.refdef.viewaxis[0][i] * cg_gun_x.value; + cg.testModelEntity.origin[i] += cg.refdef.viewaxis[1][i] * cg_gun_y.value; + cg.testModelEntity.origin[i] += cg.refdef.viewaxis[2][i] * cg_gun_z.value; + } + } + + trap_R_AddRefEntityToScene( &cg.testModelEntity ); +} + + + +//============================================================================ + + +/* +================= +CG_CalcVrect + +Sets the coordinates of the rendered window +================= +*/ +static void CG_CalcVrect (void) { + int size; + + // the intermission should allways be full screen + if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { + size = 100; + } else { + // bound normal viewsize + if (cg_viewsize.integer < 30) { + trap_Cvar_Set ("cg_viewsize","30"); + size = 30; + } else if (cg_viewsize.integer > 100) { + trap_Cvar_Set ("cg_viewsize","100"); + size = 100; + } else { + size = cg_viewsize.integer; + } + + } + cg.refdef.width = cgs.glconfig.vidWidth*size/100; + cg.refdef.width &= ~1; + + cg.refdef.height = cgs.glconfig.vidHeight*size/100; + cg.refdef.height &= ~1; + + cg.refdef.x = (cgs.glconfig.vidWidth - cg.refdef.width)/2; + cg.refdef.y = (cgs.glconfig.vidHeight - cg.refdef.height)/2; +} + +//============================================================================== + + +/* +=============== +CG_OffsetThirdPersonView + +=============== +*/ +#define FOCUS_DISTANCE 512 +static void CG_OffsetThirdPersonView( void ) { + vec3_t forward, right, up; + vec3_t view; + vec3_t focusAngles; + trace_t trace; + static vec3_t mins = { -4, -4, -4 }; + static vec3_t maxs = { 4, 4, 4 }; + vec3_t focusPoint; + float focusDist; + float forwardScale, sideScale; + + cg.refdef.vieworg[2] += cg.predictedPlayerState.viewheight; + + VectorCopy( cg.refdefViewAngles, focusAngles ); + + // if dead, look at killer + if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { + focusAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW]; + cg.refdefViewAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW]; + } + + if ( focusAngles[PITCH] > 45 ) { + focusAngles[PITCH] = 45; // don't go too far overhead + } + AngleVectors( focusAngles, forward, NULL, NULL ); + + VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint ); + + VectorCopy( cg.refdef.vieworg, view ); + + view[2] += 8; + + cg.refdefViewAngles[PITCH] *= 0.5; + + AngleVectors( cg.refdefViewAngles, forward, right, up ); + + forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI ); + sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI ); + VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view ); + VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view ); + + // trace a ray from the origin to the viewpoint to make sure the view isn't + // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything + + if (!cg_cameraMode.integer) { + CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); + + if ( trace.fraction != 1.0 ) { + VectorCopy( trace.endpos, view ); + view[2] += (1.0 - trace.fraction) * 32; + // try another trace to this position, because a tunnel may have the ceiling + // close enogh that this is poking out + + CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); + VectorCopy( trace.endpos, view ); + } + } + + + VectorCopy( view, cg.refdef.vieworg ); + + // select pitch to look at focus point from vieword + VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint ); + focusDist = sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] ); + if ( focusDist < 1 ) { + focusDist = 1; // should never happen + } + cg.refdefViewAngles[PITCH] = -180 / M_PI * atan2( focusPoint[2], focusDist ); + cg.refdefViewAngles[YAW] -= cg_thirdPersonAngle.value; +} + + +// this causes a compiler bug on mac MrC compiler +static void CG_StepOffset( void ) { + int timeDelta; + + // smooth out stair climbing + timeDelta = cg.time - cg.stepTime; + if ( timeDelta < STEP_TIME ) { + cg.refdef.vieworg[2] -= cg.stepChange + * (STEP_TIME - timeDelta) / STEP_TIME; + } +} + +/* +=============== +CG_OffsetFirstPersonView + +=============== +*/ +static void CG_OffsetFirstPersonView( void ) { + float *origin; + float *angles; + float bob; + float ratio; + float delta; + float speed; + float f; + vec3_t predictedVelocity; + int timeDelta; + + if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { + return; + } + + origin = cg.refdef.vieworg; + angles = cg.refdefViewAngles; + + // if dead, fix the angle and don't add any kick + if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) { + angles[ROLL] = 40; + angles[PITCH] = -15; + angles[YAW] = cg.snap->ps.stats[STAT_DEAD_YAW]; + origin[2] += cg.predictedPlayerState.viewheight; + return; + } + + // add angles based on weapon kick + VectorAdd (angles, cg.kick_angles, angles); + + // add angles based on damage kick + if ( cg.damageTime ) { + ratio = cg.time - cg.damageTime; + if ( ratio < DAMAGE_DEFLECT_TIME ) { + ratio /= DAMAGE_DEFLECT_TIME; + angles[PITCH] += ratio * cg.v_dmg_pitch; + angles[ROLL] += ratio * cg.v_dmg_roll; + } else { + ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME; + if ( ratio > 0 ) { + angles[PITCH] += ratio * cg.v_dmg_pitch; + angles[ROLL] += ratio * cg.v_dmg_roll; + } + } + } + + // add pitch based on fall kick +#if 0 + ratio = ( cg.time - cg.landTime) / FALL_TIME; + if (ratio < 0) + ratio = 0; + angles[PITCH] += ratio * cg.fall_value; +#endif + + // add angles based on velocity + VectorCopy( cg.predictedPlayerState.velocity, predictedVelocity ); + + delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[0]); + angles[PITCH] += delta * cg_runpitch.value; + + delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[1]); + angles[ROLL] -= delta * cg_runroll.value; + + // add angles based on bob + + // make sure the bob is visible even at low speeds + speed = cg.xyspeed > 200 ? cg.xyspeed : 200; + + delta = cg.bobfracsin * cg_bobpitch.value * speed; + if (cg.predictedPlayerState.pm_flags & PMF_DUCKED) + delta *= 3; // crouching + angles[PITCH] += delta; + delta = cg.bobfracsin * cg_bobroll.value * speed; + if (cg.predictedPlayerState.pm_flags & PMF_DUCKED) + delta *= 3; // crouching accentuates roll + if (cg.bobcycle & 1) + delta = -delta; + angles[ROLL] += delta; + +//=================================== + + // add view height + origin[2] += cg.predictedPlayerState.viewheight; + + // smooth out duck height changes + timeDelta = cg.time - cg.duckTime; + if ( timeDelta < DUCK_TIME) { + cg.refdef.vieworg[2] -= cg.duckChange + * (DUCK_TIME - timeDelta) / DUCK_TIME; + } + + // add bob height + bob = cg.bobfracsin * cg.xyspeed * cg_bobup.value; + if (bob > 6) { + bob = 6; + } + + origin[2] += bob; + + + // add fall height + delta = cg.time - cg.landTime; + if ( delta < LAND_DEFLECT_TIME ) { + f = delta / LAND_DEFLECT_TIME; + cg.refdef.vieworg[2] += cg.landChange * f; + } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) { + delta -= LAND_DEFLECT_TIME; + f = 1.0 - ( delta / LAND_RETURN_TIME ); + cg.refdef.vieworg[2] += cg.landChange * f; + } + + // add step offset + CG_StepOffset(); + + // add kick offset + + VectorAdd (origin, cg.kick_origin, origin); + + // pivot the eye based on a neck length +#if 0 + { +#define NECK_LENGTH 8 + vec3_t forward, up; + + cg.refdef.vieworg[2] -= NECK_LENGTH; + AngleVectors( cg.refdefViewAngles, forward, NULL, up ); + VectorMA( cg.refdef.vieworg, 3, forward, cg.refdef.vieworg ); + VectorMA( cg.refdef.vieworg, NECK_LENGTH, up, cg.refdef.vieworg ); + } +#endif +} + +//====================================================================== + +void CG_ZoomDown_f( void ) { + if ( cg.zoomed ) { + return; + } + cg.zoomed = qtrue; + cg.zoomTime = cg.time; +} + +void CG_ZoomUp_f( void ) { + if ( !cg.zoomed ) { + return; + } + cg.zoomed = qfalse; + cg.zoomTime = cg.time; +} + + +/* +==================== +CG_CalcFov + +Fixed fov at intermissions, otherwise account for fov variable and zooms. +==================== +*/ +#define WAVE_AMPLITUDE 1 +#define WAVE_FREQUENCY 0.4 + +static int CG_CalcFov( void ) { + float x; + float phase; + float v; + int contents; + float fov_x, fov_y; + float zoomFov; + float f; + int inwater; + + if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { + // if in intermission, use a fixed value + fov_x = 90; + } else { + // user selectable + if ( cgs.dmflags & DF_FIXED_FOV ) { + // dmflag to prevent wide fov for all clients + fov_x = 90; + } else { + fov_x = cg_fov.value; + if ( fov_x < 1 ) { + fov_x = 1; + } else if ( fov_x > 160 ) { + fov_x = 160; + } + } + + // account for zooms + zoomFov = cg_zoomFov.value; + if ( zoomFov < 1 ) { + zoomFov = 1; + } else if ( zoomFov > 160 ) { + zoomFov = 160; + } + + if ( cg.zoomed ) { + f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; + if ( f > 1.0 ) { + fov_x = zoomFov; + } else { + fov_x = fov_x + f * ( zoomFov - fov_x ); + } + } else { + f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; + if ( f > 1.0 ) { + fov_x = fov_x; + } else { + fov_x = zoomFov + f * ( fov_x - zoomFov ); + } + } + } + + x = cg.refdef.width / tan( fov_x / 360 * M_PI ); + fov_y = atan2( cg.refdef.height, x ); + fov_y = fov_y * 360 / M_PI; + + // warp if underwater + contents = CG_PointContents( cg.refdef.vieworg, -1 ); + if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ){ + phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2; + v = WAVE_AMPLITUDE * sin( phase ); + fov_x += v; + fov_y -= v; + inwater = qtrue; + } + else { + inwater = qfalse; + } + + + // set it + cg.refdef.fov_x = fov_x; + cg.refdef.fov_y = fov_y; + + if ( !cg.zoomed ) { + cg.zoomSensitivity = 1; + } else { + cg.zoomSensitivity = cg.refdef.fov_y / 75.0; + } + + return inwater; +} + + + +/* +=============== +CG_DamageBlendBlob + +=============== +*/ +static void CG_DamageBlendBlob( void ) { + int t; + int maxTime; + refEntity_t ent; + + if ( !cg.damageValue ) { + return; + } + + //if (cg.cameraMode) { + // return; + //} + + // ragePro systems can't fade blends, so don't obscure the screen + if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) { + return; + } + + maxTime = DAMAGE_TIME; + t = cg.time - cg.damageTime; + if ( t <= 0 || t >= maxTime ) { + return; + } + + + memset( &ent, 0, sizeof( ent ) ); + ent.reType = RT_SPRITE; + ent.renderfx = RF_FIRST_PERSON; + + VectorMA( cg.refdef.vieworg, 8, cg.refdef.viewaxis[0], ent.origin ); + VectorMA( ent.origin, cg.damageX * -8, cg.refdef.viewaxis[1], ent.origin ); + VectorMA( ent.origin, cg.damageY * 8, cg.refdef.viewaxis[2], ent.origin ); + + ent.radius = cg.damageValue * 3; + ent.customShader = cgs.media.viewBloodShader; + ent.shaderRGBA[0] = 255; + ent.shaderRGBA[1] = 255; + ent.shaderRGBA[2] = 255; + ent.shaderRGBA[3] = 200 * ( 1.0 - ((float)t / maxTime) ); + trap_R_AddRefEntityToScene( &ent ); +} + + +/* +=============== +CG_CalcViewValues + +Sets cg.refdef view values +=============== +*/ +static int CG_CalcViewValues( void ) { + playerState_t *ps; + + memset( &cg.refdef, 0, sizeof( cg.refdef ) ); + + // strings for in game rendering + // Q_strncpyz( cg.refdef.text[0], "Park Ranger", sizeof(cg.refdef.text[0]) ); + // Q_strncpyz( cg.refdef.text[1], "19", sizeof(cg.refdef.text[1]) ); + + // calculate size of 3D view + CG_CalcVrect(); + + ps = &cg.predictedPlayerState; +/* + if (cg.cameraMode) { + vec3_t origin, angles; + if (trap_getCameraInfo(cg.time, &origin, &angles)) { + VectorCopy(origin, cg.refdef.vieworg); + angles[ROLL] = 0; + VectorCopy(angles, cg.refdefViewAngles); + AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); + return CG_CalcFov(); + } else { + cg.cameraMode = qfalse; + } + } +*/ + // intermission view + if ( ps->pm_type == PM_INTERMISSION ) { + VectorCopy( ps->origin, cg.refdef.vieworg ); + VectorCopy( ps->viewangles, cg.refdefViewAngles ); + AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); + return CG_CalcFov(); + } + + cg.bobcycle = ( ps->bobCycle & 128 ) >> 7; + cg.bobfracsin = fabs( sin( ( ps->bobCycle & 127 ) / 127.0 * M_PI ) ); + cg.xyspeed = sqrt( ps->velocity[0] * ps->velocity[0] + + ps->velocity[1] * ps->velocity[1] ); + + + VectorCopy( ps->origin, cg.refdef.vieworg ); + VectorCopy( ps->viewangles, cg.refdefViewAngles ); + + if (cg_cameraOrbit.integer) { + if (cg.time > cg.nextOrbitTime) { + cg.nextOrbitTime = cg.time + cg_cameraOrbitDelay.integer; + cg_thirdPersonAngle.value += cg_cameraOrbit.value; + } + } + // add error decay + if ( cg_errorDecay.value > 0 ) { + int t; + float f; + + t = cg.time - cg.predictedErrorTime; + f = ( cg_errorDecay.value - t ) / cg_errorDecay.value; + if ( f > 0 && f < 1 ) { + VectorMA( cg.refdef.vieworg, f, cg.predictedError, cg.refdef.vieworg ); + } else { + cg.predictedErrorTime = 0; + } + } + + if ( cg.renderingThirdPerson ) { + // back away from character + CG_OffsetThirdPersonView(); + } else { + // offset for local bobbing and kicks + CG_OffsetFirstPersonView(); + } + + // position eye reletive to origin + AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); + + if ( cg.hyperspace ) { + cg.refdef.rdflags |= RDF_NOWORLDMODEL | RDF_HYPERSPACE; + } + + // field of view + return CG_CalcFov(); +} + + +/* +===================== +CG_PowerupTimerSounds +===================== +*/ +static void CG_PowerupTimerSounds( void ) { + int i; + int t; + + // powerup timers going away + for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { + t = cg.snap->ps.powerups[i]; + if ( t <= cg.time ) { + continue; + } + if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) { + continue; + } + if ( ( t - cg.time ) / POWERUP_BLINK_TIME != ( t - cg.oldTime ) / POWERUP_BLINK_TIME ) { + trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_ITEM, cgs.media.wearOffSound ); + } + } +} + +/* +===================== +CG_AddBufferedSound +===================== +*/ +void CG_AddBufferedSound( sfxHandle_t sfx ) { + if ( !sfx ) + return; + cg.soundBuffer[cg.soundBufferIn] = sfx; + cg.soundBufferIn = (cg.soundBufferIn + 1) % MAX_SOUNDBUFFER; + if (cg.soundBufferIn == cg.soundBufferOut) { + cg.soundBufferOut++; + } +} + +/* +===================== +CG_PlayBufferedSounds +===================== +*/ +static void CG_PlayBufferedSounds( void ) { + if ( cg.soundTime < cg.time ) { + if (cg.soundBufferOut != cg.soundBufferIn && cg.soundBuffer[cg.soundBufferOut]) { + trap_S_StartLocalSound(cg.soundBuffer[cg.soundBufferOut], CHAN_ANNOUNCER); + cg.soundBuffer[cg.soundBufferOut] = 0; + cg.soundBufferOut = (cg.soundBufferOut + 1) % MAX_SOUNDBUFFER; + cg.soundTime = cg.time + 750; + } + } +} + +//========================================================================= + +/* +================= +CG_DrawActiveFrame + +Generates and draws a game scene and status information at the given time. +================= +*/ +void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ) { + int inwater; + + cg.time = serverTime; + cg.demoPlayback = demoPlayback; + + // update cvars + CG_UpdateCvars(); + + // if we are only updating the screen as a loading + // pacifier, don't even try to read snapshots + if ( cg.infoScreenText[0] != 0 ) { + CG_DrawInformation(); + return; + } + + // any looped sounds will be respecified as entities + // are added to the render list + trap_S_ClearLoopingSounds(qfalse); + + // clear all the render lists + trap_R_ClearScene(); + + // set up cg.snap and possibly cg.nextSnap + CG_ProcessSnapshots(); + + // if we haven't received any snapshots yet, all + // we can draw is the information screen + if ( !cg.snap || ( cg.snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) { + CG_DrawInformation(); + return; + } + + // let the client system know what our weapon and zoom settings are + trap_SetUserCmdValue( cg.weaponSelect, cg.zoomSensitivity ); + + // this counter will be bumped for every valid scene we generate + cg.clientFrame++; + + // update cg.predictedPlayerState + CG_PredictPlayerState(); + + // decide on third person view + cg.renderingThirdPerson = cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0); + + // build cg.refdef + inwater = CG_CalcViewValues(); + + // first person blend blobs, done after AnglesToAxis + if ( !cg.renderingThirdPerson ) { + CG_DamageBlendBlob(); + } + + // build the render lists + if ( !cg.hyperspace ) { + CG_AddPacketEntities(); // adter calcViewValues, so predicted player state is correct + CG_AddMarks(); + CG_AddParticles (); + CG_AddLocalEntities(); + } + CG_AddViewWeapon( &cg.predictedPlayerState ); + + // add buffered sounds + CG_PlayBufferedSounds(); + + // play buffered voice chats + CG_PlayBufferedVoiceChats(); + + // finish up the rest of the refdef + if ( cg.testModelEntity.hModel ) { + CG_AddTestModel(); + } + cg.refdef.time = cg.time; + memcpy( cg.refdef.areamask, cg.snap->areamask, sizeof( cg.refdef.areamask ) ); + + // warning sounds when powerup is wearing off + CG_PowerupTimerSounds(); + + // update audio positions + trap_S_Respatialize( cg.snap->ps.clientNum, cg.refdef.vieworg, cg.refdef.viewaxis, inwater ); + + // make sure the lagometerSample and frame timing isn't done twice when in stereo + if ( stereoView != STEREO_RIGHT ) { + cg.frametime = cg.time - cg.oldTime; + if ( cg.frametime < 0 ) { + cg.frametime = 0; + } + cg.oldTime = cg.time; + CG_AddLagometerFrameInfo(); + } + if (cg_timescale.value != cg_timescaleFadeEnd.value) { + if (cg_timescale.value < cg_timescaleFadeEnd.value) { + cg_timescale.value += cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000; + if (cg_timescale.value > cg_timescaleFadeEnd.value) + cg_timescale.value = cg_timescaleFadeEnd.value; + } + else { + cg_timescale.value -= cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000; + if (cg_timescale.value < cg_timescaleFadeEnd.value) + cg_timescale.value = cg_timescaleFadeEnd.value; + } + if (cg_timescaleFadeSpeed.value) { + trap_Cvar_Set("timescale", va("%f", cg_timescale.value)); + } + } + + // actually issue the rendering calls + CG_DrawActive( stereoView ); + + if ( cg_stats.integer ) { + CG_Printf( "cg.clientFrame:%i\n", cg.clientFrame ); + } + + +} + diff --git a/reaction/engine/code/cgame/cg_weapons.c b/reaction/engine/code/cgame/cg_weapons.c new file mode 100644 index 00000000..49e2697a --- /dev/null +++ b/reaction/engine/code/cgame/cg_weapons.c @@ -0,0 +1,2277 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// cg_weapons.c -- events and effects dealing with weapons +#include "cg_local.h" + +/* +========================== +CG_MachineGunEjectBrass +========================== +*/ +static void CG_MachineGunEjectBrass( centity_t *cent ) { + localEntity_t *le; + refEntity_t *re; + vec3_t velocity, xvelocity; + vec3_t offset, xoffset; + float waterScale = 1.0f; + vec3_t v[3]; + + if ( cg_brassTime.integer <= 0 ) { + return; + } + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + velocity[0] = 0; + velocity[1] = -50 + 40 * crandom(); + velocity[2] = 100 + 50 * crandom(); + + le->leType = LE_FRAGMENT; + le->startTime = cg.time; + le->endTime = le->startTime + cg_brassTime.integer + ( cg_brassTime.integer / 4 ) * random(); + + le->pos.trType = TR_GRAVITY; + le->pos.trTime = cg.time - (rand()&15); + + AnglesToAxis( cent->lerpAngles, v ); + + offset[0] = 8; + offset[1] = -4; + offset[2] = 24; + + xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; + xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; + xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; + VectorAdd( cent->lerpOrigin, xoffset, re->origin ); + + VectorCopy( re->origin, le->pos.trBase ); + + if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { + waterScale = 0.10f; + } + + xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; + xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; + xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; + VectorScale( xvelocity, waterScale, le->pos.trDelta ); + + AxisCopy( axisDefault, re->axis ); + re->hModel = cgs.media.machinegunBrassModel; + + le->bounceFactor = 0.4 * waterScale; + + le->angles.trType = TR_LINEAR; + le->angles.trTime = cg.time; + le->angles.trBase[0] = rand()&31; + le->angles.trBase[1] = rand()&31; + le->angles.trBase[2] = rand()&31; + le->angles.trDelta[0] = 2; + le->angles.trDelta[1] = 1; + le->angles.trDelta[2] = 0; + + le->leFlags = LEF_TUMBLE; + le->leBounceSoundType = LEBS_BRASS; + le->leMarkType = LEMT_NONE; +} + +/* +========================== +CG_ShotgunEjectBrass +========================== +*/ +static void CG_ShotgunEjectBrass( centity_t *cent ) { + localEntity_t *le; + refEntity_t *re; + vec3_t velocity, xvelocity; + vec3_t offset, xoffset; + vec3_t v[3]; + int i; + + if ( cg_brassTime.integer <= 0 ) { + return; + } + + for ( i = 0; i < 2; i++ ) { + float waterScale = 1.0f; + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + velocity[0] = 60 + 60 * crandom(); + if ( i == 0 ) { + velocity[1] = 40 + 10 * crandom(); + } else { + velocity[1] = -40 + 10 * crandom(); + } + velocity[2] = 100 + 50 * crandom(); + + le->leType = LE_FRAGMENT; + le->startTime = cg.time; + le->endTime = le->startTime + cg_brassTime.integer*3 + cg_brassTime.integer * random(); + + le->pos.trType = TR_GRAVITY; + le->pos.trTime = cg.time; + + AnglesToAxis( cent->lerpAngles, v ); + + offset[0] = 8; + offset[1] = 0; + offset[2] = 24; + + xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; + xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; + xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; + VectorAdd( cent->lerpOrigin, xoffset, re->origin ); + VectorCopy( re->origin, le->pos.trBase ); + if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { + waterScale = 0.10f; + } + + xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; + xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; + xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; + VectorScale( xvelocity, waterScale, le->pos.trDelta ); + + AxisCopy( axisDefault, re->axis ); + re->hModel = cgs.media.shotgunBrassModel; + le->bounceFactor = 0.3f; + + le->angles.trType = TR_LINEAR; + le->angles.trTime = cg.time; + le->angles.trBase[0] = rand()&31; + le->angles.trBase[1] = rand()&31; + le->angles.trBase[2] = rand()&31; + le->angles.trDelta[0] = 1; + le->angles.trDelta[1] = 0.5; + le->angles.trDelta[2] = 0; + + le->leFlags = LEF_TUMBLE; + le->leBounceSoundType = LEBS_BRASS; + le->leMarkType = LEMT_NONE; + } +} + + +#ifdef MISSIONPACK +/* +========================== +CG_NailgunEjectBrass +========================== +*/ +static void CG_NailgunEjectBrass( centity_t *cent ) { + localEntity_t *smoke; + vec3_t origin; + vec3_t v[3]; + vec3_t offset; + vec3_t xoffset; + vec3_t up; + + AnglesToAxis( cent->lerpAngles, v ); + + offset[0] = 0; + offset[1] = -12; + offset[2] = 24; + + xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; + xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; + xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; + VectorAdd( cent->lerpOrigin, xoffset, origin ); + + VectorSet( up, 0, 0, 64 ); + + smoke = CG_SmokePuff( origin, up, 32, 1, 1, 1, 0.33f, 700, cg.time, 0, 0, cgs.media.smokePuffShader ); + // use the optimized local entity add + smoke->leType = LE_SCALE_FADE; +} +#endif + + +/* +========================== +CG_RailTrail +========================== +*/ +void CG_RailTrail (clientInfo_t *ci, vec3_t start, vec3_t end) { + vec3_t axis[36], move, move2, next_move, vec, temp; + float len; + int i, j, skip; + + localEntity_t *le; + refEntity_t *re; + +#define RADIUS 4 +#define ROTATION 1 +#define SPACING 5 + + start[2] -= 4; + VectorCopy (start, move); + VectorSubtract (end, start, vec); + len = VectorNormalize (vec); + PerpendicularVector(temp, vec); + for (i = 0 ; i < 36; i++) { + RotatePointAroundVector(axis[i], vec, temp, i * 10);//banshee 2.4 was 10 + } + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + le->leType = LE_FADE_RGB; + le->startTime = cg.time; + le->endTime = cg.time + cg_railTrailTime.value; + le->lifeRate = 1.0 / (le->endTime - le->startTime); + + re->shaderTime = cg.time / 1000.0f; + re->reType = RT_RAIL_CORE; + re->customShader = cgs.media.railCoreShader; + + VectorCopy(start, re->origin); + VectorCopy(end, re->oldorigin); + + re->shaderRGBA[0] = ci->color1[0] * 255; + re->shaderRGBA[1] = ci->color1[1] * 255; + re->shaderRGBA[2] = ci->color1[2] * 255; + re->shaderRGBA[3] = 255; + + le->color[0] = ci->color1[0] * 0.75; + le->color[1] = ci->color1[1] * 0.75; + le->color[2] = ci->color1[2] * 0.75; + le->color[3] = 1.0f; + + AxisClear( re->axis ); + + VectorMA(move, 20, vec, move); + VectorCopy(move, next_move); + VectorScale (vec, SPACING, vec); + + if (cg_oldRail.integer != 0) { + // nudge down a bit so it isn't exactly in center + re->origin[2] -= 8; + re->oldorigin[2] -= 8; + return; + } + skip = -1; + + j = 18; + for (i = 0; i < len; i += SPACING) { + if (i != skip) { + skip = i + SPACING; + le = CG_AllocLocalEntity(); + re = &le->refEntity; + le->leFlags = LEF_PUFF_DONT_SCALE; + le->leType = LE_MOVE_SCALE_FADE; + le->startTime = cg.time; + le->endTime = cg.time + (i>>1) + 600; + le->lifeRate = 1.0 / (le->endTime - le->startTime); + + re->shaderTime = cg.time / 1000.0f; + re->reType = RT_SPRITE; + re->radius = 1.1f; + re->customShader = cgs.media.railRingsShader; + + re->shaderRGBA[0] = ci->color2[0] * 255; + re->shaderRGBA[1] = ci->color2[1] * 255; + re->shaderRGBA[2] = ci->color2[2] * 255; + re->shaderRGBA[3] = 255; + + le->color[0] = ci->color2[0] * 0.75; + le->color[1] = ci->color2[1] * 0.75; + le->color[2] = ci->color2[2] * 0.75; + le->color[3] = 1.0f; + + le->pos.trType = TR_LINEAR; + le->pos.trTime = cg.time; + + VectorCopy( move, move2); + VectorMA(move2, RADIUS , axis[j], move2); + VectorCopy(move2, le->pos.trBase); + + le->pos.trDelta[0] = axis[j][0]*6; + le->pos.trDelta[1] = axis[j][1]*6; + le->pos.trDelta[2] = axis[j][2]*6; + } + + VectorAdd (move, vec, move); + + j = j + ROTATION < 36 ? j + ROTATION : (j + ROTATION) % 36; + } +} + +/* +========================== +CG_RocketTrail +========================== +*/ +static void CG_RocketTrail( centity_t *ent, const weaponInfo_t *wi ) { + int step; + vec3_t origin, lastPos; + int t; + int startTime, contents; + int lastContents; + entityState_t *es; + vec3_t up; + localEntity_t *smoke; + + if ( cg_noProjectileTrail.integer ) { + return; + } + + up[0] = 0; + up[1] = 0; + up[2] = 0; + + step = 50; + + es = &ent->currentState; + startTime = ent->trailTime; + t = step * ( (startTime + step) / step ); + + BG_EvaluateTrajectory( &es->pos, cg.time, origin ); + contents = CG_PointContents( origin, -1 ); + + // if object (e.g. grenade) is stationary, don't toss up smoke + if ( es->pos.trType == TR_STATIONARY ) { + ent->trailTime = cg.time; + return; + } + + BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos ); + lastContents = CG_PointContents( lastPos, -1 ); + + ent->trailTime = cg.time; + + if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { + if ( contents & lastContents & CONTENTS_WATER ) { + CG_BubbleTrail( lastPos, origin, 8 ); + } + return; + } + + for ( ; t <= ent->trailTime ; t += step ) { + BG_EvaluateTrajectory( &es->pos, t, lastPos ); + + smoke = CG_SmokePuff( lastPos, up, + wi->trailRadius, + 1, 1, 1, 0.33f, + wi->wiTrailTime, + t, + 0, + 0, + cgs.media.smokePuffShader ); + // use the optimized local entity add + smoke->leType = LE_SCALE_FADE; + } + +} + +#ifdef MISSIONPACK +/* +========================== +CG_NailTrail +========================== +*/ +static void CG_NailTrail( centity_t *ent, const weaponInfo_t *wi ) { + int step; + vec3_t origin, lastPos; + int t; + int startTime, contents; + int lastContents; + entityState_t *es; + vec3_t up; + localEntity_t *smoke; + + if ( cg_noProjectileTrail.integer ) { + return; + } + + up[0] = 0; + up[1] = 0; + up[2] = 0; + + step = 50; + + es = &ent->currentState; + startTime = ent->trailTime; + t = step * ( (startTime + step) / step ); + + BG_EvaluateTrajectory( &es->pos, cg.time, origin ); + contents = CG_PointContents( origin, -1 ); + + // if object (e.g. grenade) is stationary, don't toss up smoke + if ( es->pos.trType == TR_STATIONARY ) { + ent->trailTime = cg.time; + return; + } + + BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos ); + lastContents = CG_PointContents( lastPos, -1 ); + + ent->trailTime = cg.time; + + if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { + if ( contents & lastContents & CONTENTS_WATER ) { + CG_BubbleTrail( lastPos, origin, 8 ); + } + return; + } + + for ( ; t <= ent->trailTime ; t += step ) { + BG_EvaluateTrajectory( &es->pos, t, lastPos ); + + smoke = CG_SmokePuff( lastPos, up, + wi->trailRadius, + 1, 1, 1, 0.33f, + wi->wiTrailTime, + t, + 0, + 0, + cgs.media.nailPuffShader ); + // use the optimized local entity add + smoke->leType = LE_SCALE_FADE; + } + +} +#endif + +/* +========================== +CG_NailTrail +========================== +*/ +static void CG_PlasmaTrail( centity_t *cent, const weaponInfo_t *wi ) { + localEntity_t *le; + refEntity_t *re; + entityState_t *es; + vec3_t velocity, xvelocity, origin; + vec3_t offset, xoffset; + vec3_t v[3]; + int t, startTime, step; + + float waterScale = 1.0f; + + if ( cg_noProjectileTrail.integer || cg_oldPlasma.integer ) { + return; + } + + step = 50; + + es = ¢->currentState; + startTime = cent->trailTime; + t = step * ( (startTime + step) / step ); + + BG_EvaluateTrajectory( &es->pos, cg.time, origin ); + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + velocity[0] = 60 - 120 * crandom(); + velocity[1] = 40 - 80 * crandom(); + velocity[2] = 100 - 200 * crandom(); + + le->leType = LE_MOVE_SCALE_FADE; + le->leFlags = LEF_TUMBLE; + le->leBounceSoundType = LEBS_NONE; + le->leMarkType = LEMT_NONE; + + le->startTime = cg.time; + le->endTime = le->startTime + 600; + + le->pos.trType = TR_GRAVITY; + le->pos.trTime = cg.time; + + AnglesToAxis( cent->lerpAngles, v ); + + offset[0] = 2; + offset[1] = 2; + offset[2] = 2; + + xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; + xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; + xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; + + VectorAdd( origin, xoffset, re->origin ); + VectorCopy( re->origin, le->pos.trBase ); + + if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { + waterScale = 0.10f; + } + + xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; + xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; + xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; + VectorScale( xvelocity, waterScale, le->pos.trDelta ); + + AxisCopy( axisDefault, re->axis ); + re->shaderTime = cg.time / 1000.0f; + re->reType = RT_SPRITE; + re->radius = 0.25f; + re->customShader = cgs.media.railRingsShader; + le->bounceFactor = 0.3f; + + re->shaderRGBA[0] = wi->flashDlightColor[0] * 63; + re->shaderRGBA[1] = wi->flashDlightColor[1] * 63; + re->shaderRGBA[2] = wi->flashDlightColor[2] * 63; + re->shaderRGBA[3] = 63; + + le->color[0] = wi->flashDlightColor[0] * 0.2; + le->color[1] = wi->flashDlightColor[1] * 0.2; + le->color[2] = wi->flashDlightColor[2] * 0.2; + le->color[3] = 0.25f; + + le->angles.trType = TR_LINEAR; + le->angles.trTime = cg.time; + le->angles.trBase[0] = rand()&31; + le->angles.trBase[1] = rand()&31; + le->angles.trBase[2] = rand()&31; + le->angles.trDelta[0] = 1; + le->angles.trDelta[1] = 0.5; + le->angles.trDelta[2] = 0; + +} +/* +========================== +CG_GrappleTrail +========================== +*/ +void CG_GrappleTrail( centity_t *ent, const weaponInfo_t *wi ) { + vec3_t origin; + entityState_t *es; + vec3_t forward, up; + refEntity_t beam; + + es = &ent->currentState; + + BG_EvaluateTrajectory( &es->pos, cg.time, origin ); + ent->trailTime = cg.time; + + memset( &beam, 0, sizeof( beam ) ); + //FIXME adjust for muzzle position + VectorCopy ( cg_entities[ ent->currentState.otherEntityNum ].lerpOrigin, beam.origin ); + beam.origin[2] += 26; + AngleVectors( cg_entities[ ent->currentState.otherEntityNum ].lerpAngles, forward, NULL, up ); + VectorMA( beam.origin, -6, up, beam.origin ); + VectorCopy( origin, beam.oldorigin ); + + if (Distance( beam.origin, beam.oldorigin ) < 64 ) + return; // Don't draw if close + + beam.reType = RT_LIGHTNING; + beam.customShader = cgs.media.lightningShader; + + AxisClear( beam.axis ); + beam.shaderRGBA[0] = 0xff; + beam.shaderRGBA[1] = 0xff; + beam.shaderRGBA[2] = 0xff; + beam.shaderRGBA[3] = 0xff; + trap_R_AddRefEntityToScene( &beam ); +} + +/* +========================== +CG_GrenadeTrail +========================== +*/ +static void CG_GrenadeTrail( centity_t *ent, const weaponInfo_t *wi ) { + CG_RocketTrail( ent, wi ); +} + + +/* +================= +CG_RegisterWeapon + +The server says this item is used on this level +================= +*/ +void CG_RegisterWeapon( int weaponNum ) { + weaponInfo_t *weaponInfo; + gitem_t *item, *ammo; + char path[MAX_QPATH]; + vec3_t mins, maxs; + int i; + + weaponInfo = &cg_weapons[weaponNum]; + + if ( weaponNum == 0 ) { + return; + } + + if ( weaponInfo->registered ) { + return; + } + + memset( weaponInfo, 0, sizeof( *weaponInfo ) ); + weaponInfo->registered = qtrue; + + for ( item = bg_itemlist + 1 ; item->classname ; item++ ) { + if ( item->giType == IT_WEAPON && item->giTag == weaponNum ) { + weaponInfo->item = item; + break; + } + } + if ( !item->classname ) { + CG_Error( "Couldn't find weapon %i", weaponNum ); + } + CG_RegisterItemVisuals( item - bg_itemlist ); + + // load cmodel before model so filecache works + weaponInfo->weaponModel = trap_R_RegisterModel( item->world_model[0] ); + + // calc midpoint for rotation + trap_R_ModelBounds( weaponInfo->weaponModel, mins, maxs ); + for ( i = 0 ; i < 3 ; i++ ) { + weaponInfo->weaponMidpoint[i] = mins[i] + 0.5 * ( maxs[i] - mins[i] ); + } + + weaponInfo->weaponIcon = trap_R_RegisterShader( item->icon ); + weaponInfo->ammoIcon = trap_R_RegisterShader( item->icon ); + + for ( ammo = bg_itemlist + 1 ; ammo->classname ; ammo++ ) { + if ( ammo->giType == IT_AMMO && ammo->giTag == weaponNum ) { + break; + } + } + if ( ammo->classname && ammo->world_model[0] ) { + weaponInfo->ammoModel = trap_R_RegisterModel( ammo->world_model[0] ); + } + + strcpy( path, item->world_model[0] ); + COM_StripExtension(path, path, sizeof(path)); + strcat( path, "_flash.md3" ); + weaponInfo->flashModel = trap_R_RegisterModel( path ); + + strcpy( path, item->world_model[0] ); + COM_StripExtension(path, path, sizeof(path)); + strcat( path, "_barrel.md3" ); + weaponInfo->barrelModel = trap_R_RegisterModel( path ); + + strcpy( path, item->world_model[0] ); + COM_StripExtension(path, path, sizeof(path)); + strcat( path, "_hand.md3" ); + weaponInfo->handsModel = trap_R_RegisterModel( path ); + + if ( !weaponInfo->handsModel ) { + weaponInfo->handsModel = trap_R_RegisterModel( "models/weapons2/shotgun/shotgun_hand.md3" ); + } + + weaponInfo->loopFireSound = qfalse; + + switch ( weaponNum ) { + case WP_GAUNTLET: + MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f ); + weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/melee/fstrun.wav", qfalse ); + weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/melee/fstatck.wav", qfalse ); + break; + + case WP_LIGHTNING: + MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f ); + weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/melee/fsthum.wav", qfalse ); + weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/lightning/lg_hum.wav", qfalse ); + + weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/lightning/lg_fire.wav", qfalse ); + cgs.media.lightningShader = trap_R_RegisterShader( "lightningBoltNew"); + cgs.media.lightningExplosionModel = trap_R_RegisterModel( "models/weaphits/crackle.md3" ); + cgs.media.sfx_lghit1 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit.wav", qfalse ); + cgs.media.sfx_lghit2 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit2.wav", qfalse ); + cgs.media.sfx_lghit3 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit3.wav", qfalse ); + + break; + + case WP_GRAPPLING_HOOK: + MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f ); + weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/rocket/rocket.md3" ); + weaponInfo->missileTrailFunc = CG_GrappleTrail; + weaponInfo->missileDlight = 200; + weaponInfo->wiTrailTime = 2000; + weaponInfo->trailRadius = 64; + MAKERGB( weaponInfo->missileDlightColor, 1, 0.75f, 0 ); + weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/melee/fsthum.wav", qfalse ); + weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/melee/fstrun.wav", qfalse ); + break; + +#ifdef MISSIONPACK + case WP_CHAINGUN: + weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/vulcan/wvulfire.wav", qfalse ); + weaponInfo->loopFireSound = qtrue; + MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 ); + weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf1b.wav", qfalse ); + weaponInfo->flashSound[1] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf2b.wav", qfalse ); + weaponInfo->flashSound[2] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf3b.wav", qfalse ); + weaponInfo->flashSound[3] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf4b.wav", qfalse ); + weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass; + cgs.media.bulletExplosionShader = trap_R_RegisterShader( "bulletExplosion" ); + break; +#endif + + case WP_MACHINEGUN: + MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 ); + weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf1b.wav", qfalse ); + weaponInfo->flashSound[1] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf2b.wav", qfalse ); + weaponInfo->flashSound[2] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf3b.wav", qfalse ); + weaponInfo->flashSound[3] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf4b.wav", qfalse ); + weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass; + cgs.media.bulletExplosionShader = trap_R_RegisterShader( "bulletExplosion" ); + break; + + case WP_SHOTGUN: + MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 ); + weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/shotgun/sshotf1b.wav", qfalse ); + weaponInfo->ejectBrassFunc = CG_ShotgunEjectBrass; + break; + + case WP_ROCKET_LAUNCHER: + weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/rocket/rocket.md3" ); + weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse ); + weaponInfo->missileTrailFunc = CG_RocketTrail; + weaponInfo->missileDlight = 200; + weaponInfo->wiTrailTime = 2000; + weaponInfo->trailRadius = 64; + + MAKERGB( weaponInfo->missileDlightColor, 1, 0.75f, 0 ); + MAKERGB( weaponInfo->flashDlightColor, 1, 0.75f, 0 ); + + weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/rocket/rocklf1a.wav", qfalse ); + cgs.media.rocketExplosionShader = trap_R_RegisterShader( "rocketExplosion" ); + break; + +#ifdef MISSIONPACK + case WP_PROX_LAUNCHER: + weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/proxmine.md3" ); + weaponInfo->missileTrailFunc = CG_GrenadeTrail; + weaponInfo->wiTrailTime = 700; + weaponInfo->trailRadius = 32; + MAKERGB( weaponInfo->flashDlightColor, 1, 0.70f, 0 ); + weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/proxmine/wstbfire.wav", qfalse ); + cgs.media.grenadeExplosionShader = trap_R_RegisterShader( "grenadeExplosion" ); + break; +#endif + + case WP_GRENADE_LAUNCHER: + weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/grenade1.md3" ); + weaponInfo->missileTrailFunc = CG_GrenadeTrail; + weaponInfo->wiTrailTime = 700; + weaponInfo->trailRadius = 32; + MAKERGB( weaponInfo->flashDlightColor, 1, 0.70f, 0 ); + weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/grenade/grenlf1a.wav", qfalse ); + cgs.media.grenadeExplosionShader = trap_R_RegisterShader( "grenadeExplosion" ); + break; + +#ifdef MISSIONPACK + case WP_NAILGUN: + weaponInfo->ejectBrassFunc = CG_NailgunEjectBrass; + weaponInfo->missileTrailFunc = CG_NailTrail; +// weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/nailgun/wnalflit.wav", qfalse ); + weaponInfo->trailRadius = 16; + weaponInfo->wiTrailTime = 250; + weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/nail.md3" ); + MAKERGB( weaponInfo->flashDlightColor, 1, 0.75f, 0 ); + weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/nailgun/wnalfire.wav", qfalse ); + break; +#endif + + case WP_PLASMAGUN: +// weaponInfo->missileModel = cgs.media.invulnerabilityPowerupModel; + weaponInfo->missileTrailFunc = CG_PlasmaTrail; + weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/plasma/lasfly.wav", qfalse ); + MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f ); + weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/plasma/hyprbf1a.wav", qfalse ); + cgs.media.plasmaExplosionShader = trap_R_RegisterShader( "plasmaExplosion" ); + cgs.media.railRingsShader = trap_R_RegisterShader( "railDisc" ); + break; + + case WP_RAILGUN: + weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/railgun/rg_hum.wav", qfalse ); + MAKERGB( weaponInfo->flashDlightColor, 1, 0.5f, 0 ); + weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/railgun/railgf1a.wav", qfalse ); + cgs.media.railExplosionShader = trap_R_RegisterShader( "railExplosion" ); + cgs.media.railRingsShader = trap_R_RegisterShader( "railDisc" ); + cgs.media.railCoreShader = trap_R_RegisterShader( "railCore" ); + break; + + case WP_BFG: + weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/bfg/bfg_hum.wav", qfalse ); + MAKERGB( weaponInfo->flashDlightColor, 1, 0.7f, 1 ); + weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/bfg/bfg_fire.wav", qfalse ); + cgs.media.bfgExplosionShader = trap_R_RegisterShader( "bfgExplosion" ); + weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/bfg.md3" ); + weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse ); + break; + + default: + MAKERGB( weaponInfo->flashDlightColor, 1, 1, 1 ); + weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/rocket/rocklf1a.wav", qfalse ); + break; + } +} + +/* +================= +CG_RegisterItemVisuals + +The server says this item is used on this level +================= +*/ +void CG_RegisterItemVisuals( int itemNum ) { + itemInfo_t *itemInfo; + gitem_t *item; + + if ( itemNum < 0 || itemNum >= bg_numItems ) { + CG_Error( "CG_RegisterItemVisuals: itemNum %d out of range [0-%d]", itemNum, bg_numItems-1 ); + } + + itemInfo = &cg_items[ itemNum ]; + if ( itemInfo->registered ) { + return; + } + + item = &bg_itemlist[ itemNum ]; + + memset( itemInfo, 0, sizeof( &itemInfo ) ); + itemInfo->registered = qtrue; + + itemInfo->models[0] = trap_R_RegisterModel( item->world_model[0] ); + + itemInfo->icon = trap_R_RegisterShader( item->icon ); + + if ( item->giType == IT_WEAPON ) { + CG_RegisterWeapon( item->giTag ); + } + + // + // powerups have an accompanying ring or sphere + // + if ( item->giType == IT_POWERUP || item->giType == IT_HEALTH || + item->giType == IT_ARMOR || item->giType == IT_HOLDABLE ) { + if ( item->world_model[1] ) { + itemInfo->models[1] = trap_R_RegisterModel( item->world_model[1] ); + } + } +} + + +/* +======================================================================================== + +VIEW WEAPON + +======================================================================================== +*/ + +/* +================= +CG_MapTorsoToWeaponFrame + +================= +*/ +static int CG_MapTorsoToWeaponFrame( clientInfo_t *ci, int frame ) { + + // change weapon + if ( frame >= ci->animations[TORSO_DROP].firstFrame + && frame < ci->animations[TORSO_DROP].firstFrame + 9 ) { + return frame - ci->animations[TORSO_DROP].firstFrame + 6; + } + + // stand attack + if ( frame >= ci->animations[TORSO_ATTACK].firstFrame + && frame < ci->animations[TORSO_ATTACK].firstFrame + 6 ) { + return 1 + frame - ci->animations[TORSO_ATTACK].firstFrame; + } + + // stand attack 2 + if ( frame >= ci->animations[TORSO_ATTACK2].firstFrame + && frame < ci->animations[TORSO_ATTACK2].firstFrame + 6 ) { + return 1 + frame - ci->animations[TORSO_ATTACK2].firstFrame; + } + + return 0; +} + + +/* +============== +CG_CalculateWeaponPosition +============== +*/ +static void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles ) { + float scale; + int delta; + float fracsin; + + VectorCopy( cg.refdef.vieworg, origin ); + VectorCopy( cg.refdefViewAngles, angles ); + + // on odd legs, invert some angles + if ( cg.bobcycle & 1 ) { + scale = -cg.xyspeed; + } else { + scale = cg.xyspeed; + } + + // gun angles from bobbing + angles[ROLL] += scale * cg.bobfracsin * 0.005; + angles[YAW] += scale * cg.bobfracsin * 0.01; + angles[PITCH] += cg.xyspeed * cg.bobfracsin * 0.005; + + // drop the weapon when landing + delta = cg.time - cg.landTime; + if ( delta < LAND_DEFLECT_TIME ) { + origin[2] += cg.landChange*0.25 * delta / LAND_DEFLECT_TIME; + } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) { + origin[2] += cg.landChange*0.25 * + (LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta) / LAND_RETURN_TIME; + } + +#if 0 + // drop the weapon when stair climbing + delta = cg.time - cg.stepTime; + if ( delta < STEP_TIME/2 ) { + origin[2] -= cg.stepChange*0.25 * delta / (STEP_TIME/2); + } else if ( delta < STEP_TIME ) { + origin[2] -= cg.stepChange*0.25 * (STEP_TIME - delta) / (STEP_TIME/2); + } +#endif + + // idle drift + scale = cg.xyspeed + 40; + fracsin = sin( cg.time * 0.001 ); + angles[ROLL] += scale * fracsin * 0.01; + angles[YAW] += scale * fracsin * 0.01; + angles[PITCH] += scale * fracsin * 0.01; +} + + +/* +=============== +CG_LightningBolt + +Origin will be the exact tag point, which is slightly +different than the muzzle point used for determining hits. +The cent should be the non-predicted cent if it is from the player, +so the endpoint will reflect the simulated strike (lagging the predicted +angle) +=============== +*/ +static void CG_LightningBolt( centity_t *cent, vec3_t origin ) { + trace_t trace; + refEntity_t beam; + vec3_t forward; + vec3_t muzzlePoint, endPoint; + + if (cent->currentState.weapon != WP_LIGHTNING) { + return; + } + + memset( &beam, 0, sizeof( beam ) ); + + // CPMA "true" lightning + if ((cent->currentState.number == cg.predictedPlayerState.clientNum) && (cg_trueLightning.value != 0)) { + vec3_t angle; + int i; + + for (i = 0; i < 3; i++) { + float a = cent->lerpAngles[i] - cg.refdefViewAngles[i]; + if (a > 180) { + a -= 360; + } + if (a < -180) { + a += 360; + } + + angle[i] = cg.refdefViewAngles[i] + a * (1.0 - cg_trueLightning.value); + if (angle[i] < 0) { + angle[i] += 360; + } + if (angle[i] > 360) { + angle[i] -= 360; + } + } + + AngleVectors(angle, forward, NULL, NULL ); + VectorCopy(cent->lerpOrigin, muzzlePoint ); +// VectorCopy(cg.refdef.vieworg, muzzlePoint ); + } else { + // !CPMA + AngleVectors( cent->lerpAngles, forward, NULL, NULL ); + VectorCopy(cent->lerpOrigin, muzzlePoint ); + } + + // FIXME: crouch + muzzlePoint[2] += DEFAULT_VIEWHEIGHT; + + VectorMA( muzzlePoint, 14, forward, muzzlePoint ); + + // project forward by the lightning range + VectorMA( muzzlePoint, LIGHTNING_RANGE, forward, endPoint ); + + // see if it hit a wall + CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint, + cent->currentState.number, MASK_SHOT ); + + // this is the endpoint + VectorCopy( trace.endpos, beam.oldorigin ); + + // use the provided origin, even though it may be slightly + // different than the muzzle origin + VectorCopy( origin, beam.origin ); + + beam.reType = RT_LIGHTNING; + beam.customShader = cgs.media.lightningShader; + trap_R_AddRefEntityToScene( &beam ); + + // add the impact flare if it hit something + if ( trace.fraction < 1.0 ) { + vec3_t angles; + vec3_t dir; + + VectorSubtract( beam.oldorigin, beam.origin, dir ); + VectorNormalize( dir ); + + memset( &beam, 0, sizeof( beam ) ); + beam.hModel = cgs.media.lightningExplosionModel; + + VectorMA( trace.endpos, -16, dir, beam.origin ); + + // make a random orientation + angles[0] = rand() % 360; + angles[1] = rand() % 360; + angles[2] = rand() % 360; + AnglesToAxis( angles, beam.axis ); + trap_R_AddRefEntityToScene( &beam ); + } +} +/* + +static void CG_LightningBolt( centity_t *cent, vec3_t origin ) { + trace_t trace; + refEntity_t beam; + vec3_t forward; + vec3_t muzzlePoint, endPoint; + + if ( cent->currentState.weapon != WP_LIGHTNING ) { + return; + } + + memset( &beam, 0, sizeof( beam ) ); + + // find muzzle point for this frame + VectorCopy( cent->lerpOrigin, muzzlePoint ); + AngleVectors( cent->lerpAngles, forward, NULL, NULL ); + + // FIXME: crouch + muzzlePoint[2] += DEFAULT_VIEWHEIGHT; + + VectorMA( muzzlePoint, 14, forward, muzzlePoint ); + + // project forward by the lightning range + VectorMA( muzzlePoint, LIGHTNING_RANGE, forward, endPoint ); + + // see if it hit a wall + CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint, + cent->currentState.number, MASK_SHOT ); + + // this is the endpoint + VectorCopy( trace.endpos, beam.oldorigin ); + + // use the provided origin, even though it may be slightly + // different than the muzzle origin + VectorCopy( origin, beam.origin ); + + beam.reType = RT_LIGHTNING; + beam.customShader = cgs.media.lightningShader; + trap_R_AddRefEntityToScene( &beam ); + + // add the impact flare if it hit something + if ( trace.fraction < 1.0 ) { + vec3_t angles; + vec3_t dir; + + VectorSubtract( beam.oldorigin, beam.origin, dir ); + VectorNormalize( dir ); + + memset( &beam, 0, sizeof( beam ) ); + beam.hModel = cgs.media.lightningExplosionModel; + + VectorMA( trace.endpos, -16, dir, beam.origin ); + + // make a random orientation + angles[0] = rand() % 360; + angles[1] = rand() % 360; + angles[2] = rand() % 360; + AnglesToAxis( angles, beam.axis ); + trap_R_AddRefEntityToScene( &beam ); + } +} +*/ + +/* +=============== +CG_SpawnRailTrail + +Origin will be the exact tag point, which is slightly +different than the muzzle point used for determining hits. +=============== +*/ +static void CG_SpawnRailTrail( centity_t *cent, vec3_t origin ) { + clientInfo_t *ci; + + if ( cent->currentState.weapon != WP_RAILGUN ) { + return; + } + if ( !cent->pe.railgunFlash ) { + return; + } + cent->pe.railgunFlash = qtrue; + ci = &cgs.clientinfo[ cent->currentState.clientNum ]; + CG_RailTrail( ci, origin, cent->pe.railgunImpact ); +} + + +/* +====================== +CG_MachinegunSpinAngle +====================== +*/ +#define SPIN_SPEED 0.9 +#define COAST_TIME 1000 +static float CG_MachinegunSpinAngle( centity_t *cent ) { + int delta; + float angle; + float speed; + + delta = cg.time - cent->pe.barrelTime; + if ( cent->pe.barrelSpinning ) { + angle = cent->pe.barrelAngle + delta * SPIN_SPEED; + } else { + if ( delta > COAST_TIME ) { + delta = COAST_TIME; + } + + speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME ); + angle = cent->pe.barrelAngle + delta * speed; + } + + if ( cent->pe.barrelSpinning == !(cent->currentState.eFlags & EF_FIRING) ) { + cent->pe.barrelTime = cg.time; + cent->pe.barrelAngle = AngleMod( angle ); + cent->pe.barrelSpinning = !!(cent->currentState.eFlags & EF_FIRING); +#ifdef MISSIONPACK + if ( cent->currentState.weapon == WP_CHAINGUN && !cent->pe.barrelSpinning ) { + trap_S_StartSound( NULL, cent->currentState.number, CHAN_WEAPON, trap_S_RegisterSound( "sound/weapons/vulcan/wvulwind.wav", qfalse ) ); + } +#endif + } + + return angle; +} + + +/* +======================== +CG_AddWeaponWithPowerups +======================== +*/ +static void CG_AddWeaponWithPowerups( refEntity_t *gun, int powerups ) { + // add powerup effects + if ( powerups & ( 1 << PW_INVIS ) ) { + gun->customShader = cgs.media.invisShader; + trap_R_AddRefEntityToScene( gun ); + } else { + trap_R_AddRefEntityToScene( gun ); + + if ( powerups & ( 1 << PW_BATTLESUIT ) ) { + gun->customShader = cgs.media.battleWeaponShader; + trap_R_AddRefEntityToScene( gun ); + } + if ( powerups & ( 1 << PW_QUAD ) ) { + gun->customShader = cgs.media.quadWeaponShader; + trap_R_AddRefEntityToScene( gun ); + } + } +} + + +/* +============= +CG_AddPlayerWeapon + +Used for both the view weapon (ps is valid) and the world modelother character models (ps is NULL) +The main player will have this called for BOTH cases, so effects like light and +sound should only be done on the world model case. +============= +*/ +void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent, int team ) { + refEntity_t gun; + refEntity_t barrel; + refEntity_t flash; + vec3_t angles; + weapon_t weaponNum; + weaponInfo_t *weapon; + centity_t *nonPredictedCent; +// int col; + + weaponNum = cent->currentState.weapon; + + CG_RegisterWeapon( weaponNum ); + weapon = &cg_weapons[weaponNum]; + + // add the weapon + memset( &gun, 0, sizeof( gun ) ); + VectorCopy( parent->lightingOrigin, gun.lightingOrigin ); + gun.shadowPlane = parent->shadowPlane; + gun.renderfx = parent->renderfx; + + // set custom shading for railgun refire rate + if ( ps ) { + if ( cg.predictedPlayerState.weapon == WP_RAILGUN + && cg.predictedPlayerState.weaponstate == WEAPON_FIRING ) { + float f; + + f = (float)cg.predictedPlayerState.weaponTime / 1500; + gun.shaderRGBA[1] = 0; + gun.shaderRGBA[0] = + gun.shaderRGBA[2] = 255 * ( 1.0 - f ); + } else { + gun.shaderRGBA[0] = 255; + gun.shaderRGBA[1] = 255; + gun.shaderRGBA[2] = 255; + gun.shaderRGBA[3] = 255; + } + } + + gun.hModel = weapon->weaponModel; + if (!gun.hModel) { + return; + } + + if ( !ps ) { + // add weapon ready sound + cent->pe.lightningFiring = qfalse; + if ( ( cent->currentState.eFlags & EF_FIRING ) && weapon->firingSound ) { + // lightning gun and guantlet make a different sound when fire is held down + trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->firingSound ); + cent->pe.lightningFiring = qtrue; + } else if ( weapon->readySound ) { + trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->readySound ); + } + } + + CG_PositionEntityOnTag( &gun, parent, parent->hModel, "tag_weapon"); + + CG_AddWeaponWithPowerups( &gun, cent->currentState.powerups ); + + // add the spinning barrel + if ( weapon->barrelModel ) { + memset( &barrel, 0, sizeof( barrel ) ); + VectorCopy( parent->lightingOrigin, barrel.lightingOrigin ); + barrel.shadowPlane = parent->shadowPlane; + barrel.renderfx = parent->renderfx; + + barrel.hModel = weapon->barrelModel; + angles[YAW] = 0; + angles[PITCH] = 0; + angles[ROLL] = CG_MachinegunSpinAngle( cent ); + AnglesToAxis( angles, barrel.axis ); + + CG_PositionRotatedEntityOnTag( &barrel, &gun, weapon->weaponModel, "tag_barrel" ); + + CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups ); + } + + // make sure we aren't looking at cg.predictedPlayerEntity for LG + nonPredictedCent = &cg_entities[cent->currentState.clientNum]; + + // if the index of the nonPredictedCent is not the same as the clientNum + // then this is a fake player (like on teh single player podiums), so + // go ahead and use the cent + if( ( nonPredictedCent - cg_entities ) != cent->currentState.clientNum ) { + nonPredictedCent = cent; + } + + // add the flash + if ( ( weaponNum == WP_LIGHTNING || weaponNum == WP_GAUNTLET || weaponNum == WP_GRAPPLING_HOOK ) + && ( nonPredictedCent->currentState.eFlags & EF_FIRING ) ) + { + // continuous flash + } else { + // impulse flash + if ( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME && !cent->pe.railgunFlash ) { + return; + } + } + + memset( &flash, 0, sizeof( flash ) ); + VectorCopy( parent->lightingOrigin, flash.lightingOrigin ); + flash.shadowPlane = parent->shadowPlane; + flash.renderfx = parent->renderfx; + + flash.hModel = weapon->flashModel; + if (!flash.hModel) { + return; + } + angles[YAW] = 0; + angles[PITCH] = 0; + angles[ROLL] = crandom() * 10; + AnglesToAxis( angles, flash.axis ); + + // colorize the railgun blast + if ( weaponNum == WP_RAILGUN ) { + clientInfo_t *ci; + + ci = &cgs.clientinfo[ cent->currentState.clientNum ]; + flash.shaderRGBA[0] = 255 * ci->color1[0]; + flash.shaderRGBA[1] = 255 * ci->color1[1]; + flash.shaderRGBA[2] = 255 * ci->color1[2]; + } + + CG_PositionRotatedEntityOnTag( &flash, &gun, weapon->weaponModel, "tag_flash"); + trap_R_AddRefEntityToScene( &flash ); + + if ( ps || cg.renderingThirdPerson || + cent->currentState.number != cg.predictedPlayerState.clientNum ) { + // add lightning bolt + CG_LightningBolt( nonPredictedCent, flash.origin ); + + // add rail trail + CG_SpawnRailTrail( cent, flash.origin ); + + if ( weapon->flashDlightColor[0] || weapon->flashDlightColor[1] || weapon->flashDlightColor[2] ) { + trap_R_AddLightToScene( flash.origin, 300 + (rand()&31), weapon->flashDlightColor[0], + weapon->flashDlightColor[1], weapon->flashDlightColor[2] ); + } + } +} + +/* +============== +CG_AddViewWeapon + +Add the weapon, and flash for the player's view +============== +*/ +void CG_AddViewWeapon( playerState_t *ps ) { + refEntity_t hand; + centity_t *cent; + clientInfo_t *ci; + float fovOffset; + vec3_t angles; + weaponInfo_t *weapon; + + if ( ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) { + return; + } + + if ( ps->pm_type == PM_INTERMISSION ) { + return; + } + + // no gun if in third person view or a camera is active + //if ( cg.renderingThirdPerson || cg.cameraMode) { + if ( cg.renderingThirdPerson ) { + return; + } + + + // allow the gun to be completely removed + if ( !cg_drawGun.integer ) { + vec3_t origin; + + if ( cg.predictedPlayerState.eFlags & EF_FIRING ) { + // special hack for lightning gun... + VectorCopy( cg.refdef.vieworg, origin ); + VectorMA( origin, -8, cg.refdef.viewaxis[2], origin ); + CG_LightningBolt( &cg_entities[ps->clientNum], origin ); + } + return; + } + + // don't draw if testing a gun model + if ( cg.testGun ) { + return; + } + + // drop gun lower at higher fov + if ( cg_fov.integer > 90 ) { + fovOffset = -0.2 * ( cg_fov.integer - 90 ); + } else { + fovOffset = 0; + } + + cent = &cg.predictedPlayerEntity; // &cg_entities[cg.snap->ps.clientNum]; + CG_RegisterWeapon( ps->weapon ); + weapon = &cg_weapons[ ps->weapon ]; + + memset (&hand, 0, sizeof(hand)); + + // set up gun position + CG_CalculateWeaponPosition( hand.origin, angles ); + + VectorMA( hand.origin, cg_gun_x.value, cg.refdef.viewaxis[0], hand.origin ); + VectorMA( hand.origin, cg_gun_y.value, cg.refdef.viewaxis[1], hand.origin ); + VectorMA( hand.origin, (cg_gun_z.value+fovOffset), cg.refdef.viewaxis[2], hand.origin ); + + AnglesToAxis( angles, hand.axis ); + + // map torso animations to weapon animations + if ( cg_gun_frame.integer ) { + // development tool + hand.frame = hand.oldframe = cg_gun_frame.integer; + hand.backlerp = 0; + } else { + // get clientinfo for animation map + ci = &cgs.clientinfo[ cent->currentState.clientNum ]; + hand.frame = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.frame ); + hand.oldframe = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.oldFrame ); + hand.backlerp = cent->pe.torso.backlerp; + } + + hand.hModel = weapon->handsModel; + hand.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON | RF_MINLIGHT; + + // add everything onto the hand + CG_AddPlayerWeapon( &hand, ps, &cg.predictedPlayerEntity, ps->persistant[PERS_TEAM] ); +} + +/* +============================================================================== + +WEAPON SELECTION + +============================================================================== +*/ + +/* +=================== +CG_DrawWeaponSelect +=================== +*/ +void CG_DrawWeaponSelect( void ) { + int i; + int bits; + int count; + int x, y, w; + char *name; + float *color; + + // don't display if dead + if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { + return; + } + + color = CG_FadeColor( cg.weaponSelectTime, WEAPON_SELECT_TIME ); + if ( !color ) { + return; + } + trap_R_SetColor( color ); + + // showing weapon select clears pickup item display, but not the blend blob + cg.itemPickupTime = 0; + + // count the number of weapons owned + bits = cg.snap->ps.stats[ STAT_WEAPONS ]; + count = 0; + for ( i = 1 ; i < MAX_WEAPONS ; i++ ) { + if ( bits & ( 1 << i ) ) { + count++; + } + } + + x = 320 - count * 20; + y = 380; + + for ( i = 1 ; i < MAX_WEAPONS ; i++ ) { + if ( !( bits & ( 1 << i ) ) ) { + continue; + } + + CG_RegisterWeapon( i ); + + // draw weapon icon + CG_DrawPic( x, y, 32, 32, cg_weapons[i].weaponIcon ); + + // draw selection marker + if ( i == cg.weaponSelect ) { + CG_DrawPic( x-4, y-4, 40, 40, cgs.media.selectShader ); + } + + // no ammo cross on top + if ( !cg.snap->ps.ammo[ i ] ) { + CG_DrawPic( x, y, 32, 32, cgs.media.noammoShader ); + } + + x += 40; + } + + // draw the selected name + if ( cg_weapons[ cg.weaponSelect ].item ) { + name = cg_weapons[ cg.weaponSelect ].item->pickup_name; + if ( name ) { + w = CG_DrawStrlen( name ) * BIGCHAR_WIDTH; + x = ( SCREEN_WIDTH - w ) / 2; + CG_DrawBigStringColor(x, y - 22, name, color); + } + } + + trap_R_SetColor( NULL ); +} + + +/* +=============== +CG_WeaponSelectable +=============== +*/ +static qboolean CG_WeaponSelectable( int i ) { + if ( !cg.snap->ps.ammo[i] ) { + return qfalse; + } + if ( ! (cg.snap->ps.stats[ STAT_WEAPONS ] & ( 1 << i ) ) ) { + return qfalse; + } + + return qtrue; +} + +/* +=============== +CG_NextWeapon_f +=============== +*/ +void CG_NextWeapon_f( void ) { + int i; + int original; + + if ( !cg.snap ) { + return; + } + if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { + return; + } + + cg.weaponSelectTime = cg.time; + original = cg.weaponSelect; + + for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { + cg.weaponSelect++; + if ( cg.weaponSelect == MAX_WEAPONS ) { + cg.weaponSelect = 0; + } + if ( cg.weaponSelect == WP_GAUNTLET ) { + continue; // never cycle to gauntlet + } + if ( CG_WeaponSelectable( cg.weaponSelect ) ) { + break; + } + } + if ( i == MAX_WEAPONS ) { + cg.weaponSelect = original; + } +} + +/* +=============== +CG_PrevWeapon_f +=============== +*/ +void CG_PrevWeapon_f( void ) { + int i; + int original; + + if ( !cg.snap ) { + return; + } + if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { + return; + } + + cg.weaponSelectTime = cg.time; + original = cg.weaponSelect; + + for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { + cg.weaponSelect--; + if ( cg.weaponSelect == -1 ) { + cg.weaponSelect = MAX_WEAPONS - 1; + } + if ( cg.weaponSelect == WP_GAUNTLET ) { + continue; // never cycle to gauntlet + } + if ( CG_WeaponSelectable( cg.weaponSelect ) ) { + break; + } + } + if ( i == MAX_WEAPONS ) { + cg.weaponSelect = original; + } +} + +/* +=============== +CG_Weapon_f +=============== +*/ +void CG_Weapon_f( void ) { + int num; + + if ( !cg.snap ) { + return; + } + if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { + return; + } + + num = atoi( CG_Argv( 1 ) ); + + if ( num < 1 || num > MAX_WEAPONS-1 ) { + return; + } + + cg.weaponSelectTime = cg.time; + + if ( ! ( cg.snap->ps.stats[STAT_WEAPONS] & ( 1 << num ) ) ) { + return; // don't have the weapon + } + + cg.weaponSelect = num; +} + +/* +=================== +CG_OutOfAmmoChange + +The current weapon has just run out of ammo +=================== +*/ +void CG_OutOfAmmoChange( void ) { + int i; + + cg.weaponSelectTime = cg.time; + + for ( i = MAX_WEAPONS-1 ; i > 0 ; i-- ) { + if ( CG_WeaponSelectable( i ) ) { + cg.weaponSelect = i; + break; + } + } +} + + + +/* +=================================================================================================== + +WEAPON EVENTS + +=================================================================================================== +*/ + +/* +================ +CG_FireWeapon + +Caused by an EV_FIRE_WEAPON event +================ +*/ +void CG_FireWeapon( centity_t *cent ) { + entityState_t *ent; + int c; + weaponInfo_t *weap; + + ent = ¢->currentState; + if ( ent->weapon == WP_NONE ) { + return; + } + if ( ent->weapon >= WP_NUM_WEAPONS ) { + CG_Error( "CG_FireWeapon: ent->weapon >= WP_NUM_WEAPONS" ); + return; + } + weap = &cg_weapons[ ent->weapon ]; + + // mark the entity as muzzle flashing, so when it is added it will + // append the flash to the weapon model + cent->muzzleFlashTime = cg.time; + + // lightning gun only does this this on initial press + if ( ent->weapon == WP_LIGHTNING ) { + if ( cent->pe.lightningFiring ) { + return; + } + } + + // play quad sound if needed + if ( cent->currentState.powerups & ( 1 << PW_QUAD ) ) { + trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.media.quadSound ); + } + + // play a sound + for ( c = 0 ; c < 4 ; c++ ) { + if ( !weap->flashSound[c] ) { + break; + } + } + if ( c > 0 ) { + c = rand() % c; + if ( weap->flashSound[c] ) + { + trap_S_StartSound( NULL, ent->number, CHAN_WEAPON, weap->flashSound[c] ); + } + } + + // do brass ejection + if ( weap->ejectBrassFunc && cg_brassTime.integer > 0 ) { + weap->ejectBrassFunc( cent ); + } +} + + +/* +================= +CG_MissileHitWall + +Caused by an EV_MISSILE_MISS event, or directly by local bullet tracing +================= +*/ +void CG_MissileHitWall( int weapon, int clientNum, vec3_t origin, vec3_t dir, impactSound_t soundType ) { + qhandle_t mod; + qhandle_t mark; + qhandle_t shader; + sfxHandle_t sfx; + float radius; + float light; + vec3_t lightColor; + localEntity_t *le; + int r; + qboolean alphaFade; + qboolean isSprite; + int duration; + vec3_t sprOrg; + vec3_t sprVel; + + mark = 0; + radius = 32; + sfx = 0; + mod = 0; + shader = 0; + light = 0; + lightColor[0] = 1; + lightColor[1] = 1; + lightColor[2] = 0; + + // set defaults + isSprite = qfalse; + duration = 600; + + switch ( weapon ) { + default: +#ifdef MISSIONPACK + case WP_NAILGUN: + if( soundType == IMPACTSOUND_FLESH ) { + sfx = cgs.media.sfx_nghitflesh; + } else if( soundType == IMPACTSOUND_METAL ) { + sfx = cgs.media.sfx_nghitmetal; + } else { + sfx = cgs.media.sfx_nghit; + } + mark = cgs.media.holeMarkShader; + radius = 12; + break; +#endif + case WP_LIGHTNING: + // no explosion at LG impact, it is added with the beam + r = rand() & 3; + if ( r < 2 ) { + sfx = cgs.media.sfx_lghit2; + } else if ( r == 2 ) { + sfx = cgs.media.sfx_lghit1; + } else { + sfx = cgs.media.sfx_lghit3; + } + mark = cgs.media.holeMarkShader; + radius = 12; + break; +#ifdef MISSIONPACK + case WP_PROX_LAUNCHER: + mod = cgs.media.dishFlashModel; + shader = cgs.media.grenadeExplosionShader; + sfx = cgs.media.sfx_proxexp; + mark = cgs.media.burnMarkShader; + radius = 64; + light = 300; + isSprite = qtrue; + break; +#endif + case WP_GRENADE_LAUNCHER: + mod = cgs.media.dishFlashModel; + shader = cgs.media.grenadeExplosionShader; + sfx = cgs.media.sfx_rockexp; + mark = cgs.media.burnMarkShader; + radius = 64; + light = 300; + isSprite = qtrue; + break; + case WP_ROCKET_LAUNCHER: + mod = cgs.media.dishFlashModel; + shader = cgs.media.rocketExplosionShader; + sfx = cgs.media.sfx_rockexp; + mark = cgs.media.burnMarkShader; + radius = 64; + light = 300; + isSprite = qtrue; + duration = 1000; + lightColor[0] = 1; + lightColor[1] = 0.75; + lightColor[2] = 0.0; + if (cg_oldRocket.integer == 0) { + // explosion sprite animation + VectorMA( origin, 24, dir, sprOrg ); + VectorScale( dir, 64, sprVel ); + + CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1400, 20, 30 ); + } + break; + case WP_RAILGUN: + mod = cgs.media.ringFlashModel; + shader = cgs.media.railExplosionShader; + sfx = cgs.media.sfx_plasmaexp; + mark = cgs.media.energyMarkShader; + radius = 24; + break; + case WP_PLASMAGUN: + mod = cgs.media.ringFlashModel; + shader = cgs.media.plasmaExplosionShader; + sfx = cgs.media.sfx_plasmaexp; + mark = cgs.media.energyMarkShader; + radius = 16; + break; + case WP_BFG: + mod = cgs.media.dishFlashModel; + shader = cgs.media.bfgExplosionShader; + sfx = cgs.media.sfx_rockexp; + mark = cgs.media.burnMarkShader; + radius = 32; + isSprite = qtrue; + break; + case WP_SHOTGUN: + mod = cgs.media.bulletFlashModel; + shader = cgs.media.bulletExplosionShader; + mark = cgs.media.bulletMarkShader; + sfx = 0; + radius = 4; + break; + +#ifdef MISSIONPACK + case WP_CHAINGUN: + mod = cgs.media.bulletFlashModel; + if( soundType == IMPACTSOUND_FLESH ) { + sfx = cgs.media.sfx_chghitflesh; + } else if( soundType == IMPACTSOUND_METAL ) { + sfx = cgs.media.sfx_chghitmetal; + } else { + sfx = cgs.media.sfx_chghit; + } + mark = cgs.media.bulletMarkShader; + + r = rand() & 3; + if ( r < 2 ) { + sfx = cgs.media.sfx_ric1; + } else if ( r == 2 ) { + sfx = cgs.media.sfx_ric2; + } else { + sfx = cgs.media.sfx_ric3; + } + + radius = 8; + break; +#endif + + case WP_MACHINEGUN: + mod = cgs.media.bulletFlashModel; + shader = cgs.media.bulletExplosionShader; + mark = cgs.media.bulletMarkShader; + + r = rand() & 3; + if ( r == 0 ) { + sfx = cgs.media.sfx_ric1; + } else if ( r == 1 ) { + sfx = cgs.media.sfx_ric2; + } else { + sfx = cgs.media.sfx_ric3; + } + + radius = 8; + break; + } + + if ( sfx ) { + trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, sfx ); + } + + // + // create the explosion + // + if ( mod ) { + le = CG_MakeExplosion( origin, dir, + mod, shader, + duration, isSprite ); + le->light = light; + VectorCopy( lightColor, le->lightColor ); + if ( weapon == WP_RAILGUN ) { + // colorize with client color + VectorCopy( cgs.clientinfo[clientNum].color1, le->color ); + } + } + + // + // impact mark + // + alphaFade = (mark == cgs.media.energyMarkShader); // plasma fades alpha, all others fade color + if ( weapon == WP_RAILGUN ) { + float *color; + + // colorize with client color + color = cgs.clientinfo[clientNum].color2; + CG_ImpactMark( mark, origin, dir, random()*360, color[0],color[1], color[2],1, alphaFade, radius, qfalse ); + } else { + CG_ImpactMark( mark, origin, dir, random()*360, 1,1,1,1, alphaFade, radius, qfalse ); + } +} + + +/* +================= +CG_MissileHitPlayer +================= +*/ +void CG_MissileHitPlayer( int weapon, vec3_t origin, vec3_t dir, int entityNum ) { + CG_Bleed( origin, entityNum ); + + // some weapons will make an explosion with the blood, while + // others will just make the blood + switch ( weapon ) { + case WP_GRENADE_LAUNCHER: + case WP_ROCKET_LAUNCHER: +#ifdef MISSIONPACK + case WP_NAILGUN: + case WP_CHAINGUN: + case WP_PROX_LAUNCHER: +#endif + CG_MissileHitWall( weapon, 0, origin, dir, IMPACTSOUND_FLESH ); + break; + default: + break; + } +} + + + +/* +============================================================================ + +SHOTGUN TRACING + +============================================================================ +*/ + +/* +================ +CG_ShotgunPellet +================ +*/ +static void CG_ShotgunPellet( vec3_t start, vec3_t end, int skipNum ) { + trace_t tr; + int sourceContentType, destContentType; + + CG_Trace( &tr, start, NULL, NULL, end, skipNum, MASK_SHOT ); + + sourceContentType = trap_CM_PointContents( start, 0 ); + destContentType = trap_CM_PointContents( tr.endpos, 0 ); + + // FIXME: should probably move this cruft into CG_BubbleTrail + if ( sourceContentType == destContentType ) { + if ( sourceContentType & CONTENTS_WATER ) { + CG_BubbleTrail( start, tr.endpos, 32 ); + } + } else if ( sourceContentType & CONTENTS_WATER ) { + trace_t trace; + + trap_CM_BoxTrace( &trace, end, start, NULL, NULL, 0, CONTENTS_WATER ); + CG_BubbleTrail( start, trace.endpos, 32 ); + } else if ( destContentType & CONTENTS_WATER ) { + trace_t trace; + + trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, CONTENTS_WATER ); + CG_BubbleTrail( tr.endpos, trace.endpos, 32 ); + } + + if ( tr.surfaceFlags & SURF_NOIMPACT ) { + return; + } + + if ( cg_entities[tr.entityNum].currentState.eType == ET_PLAYER ) { + CG_MissileHitPlayer( WP_SHOTGUN, tr.endpos, tr.plane.normal, tr.entityNum ); + } else { + if ( tr.surfaceFlags & SURF_NOIMPACT ) { + // SURF_NOIMPACT will not make a flame puff or a mark + return; + } + if ( tr.surfaceFlags & SURF_METALSTEPS ) { + CG_MissileHitWall( WP_SHOTGUN, 0, tr.endpos, tr.plane.normal, IMPACTSOUND_METAL ); + } else { + CG_MissileHitWall( WP_SHOTGUN, 0, tr.endpos, tr.plane.normal, IMPACTSOUND_DEFAULT ); + } + } +} + +/* +================ +CG_ShotgunPattern + +Perform the same traces the server did to locate the +hit splashes +================ +*/ +static void CG_ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, int otherEntNum ) { + int i; + float r, u; + vec3_t end; + vec3_t forward, right, up; + + // derive the right and up vectors from the forward vector, because + // the client won't have any other information + VectorNormalize2( origin2, forward ); + PerpendicularVector( right, forward ); + CrossProduct( forward, right, up ); + + // generate the "random" spread pattern + for ( i = 0 ; i < DEFAULT_SHOTGUN_COUNT ; i++ ) { + r = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16; + u = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16; + VectorMA( origin, 8192 * 16, forward, end); + VectorMA (end, r, right, end); + VectorMA (end, u, up, end); + + CG_ShotgunPellet( origin, end, otherEntNum ); + } +} + +/* +============== +CG_ShotgunFire +============== +*/ +void CG_ShotgunFire( entityState_t *es ) { + vec3_t v; + int contents; + + VectorSubtract( es->origin2, es->pos.trBase, v ); + VectorNormalize( v ); + VectorScale( v, 32, v ); + VectorAdd( es->pos.trBase, v, v ); + if ( cgs.glconfig.hardwareType != GLHW_RAGEPRO ) { + // ragepro can't alpha fade, so don't even bother with smoke + vec3_t up; + + contents = trap_CM_PointContents( es->pos.trBase, 0 ); + if ( !( contents & CONTENTS_WATER ) ) { + VectorSet( up, 0, 0, 8 ); + CG_SmokePuff( v, up, 32, 1, 1, 1, 0.33f, 900, cg.time, 0, LEF_PUFF_DONT_SCALE, cgs.media.shotgunSmokePuffShader ); + } + } + CG_ShotgunPattern( es->pos.trBase, es->origin2, es->eventParm, es->otherEntityNum ); +} + +/* +============================================================================ + +BULLETS + +============================================================================ +*/ + + +/* +=============== +CG_Tracer +=============== +*/ +void CG_Tracer( vec3_t source, vec3_t dest ) { + vec3_t forward, right; + polyVert_t verts[4]; + vec3_t line; + float len, begin, end; + vec3_t start, finish; + vec3_t midpoint; + + // tracer + VectorSubtract( dest, source, forward ); + len = VectorNormalize( forward ); + + // start at least a little ways from the muzzle + if ( len < 100 ) { + return; + } + begin = 50 + random() * (len - 60); + end = begin + cg_tracerLength.value; + if ( end > len ) { + end = len; + } + VectorMA( source, begin, forward, start ); + VectorMA( source, end, forward, finish ); + + line[0] = DotProduct( forward, cg.refdef.viewaxis[1] ); + line[1] = DotProduct( forward, cg.refdef.viewaxis[2] ); + + VectorScale( cg.refdef.viewaxis[1], line[1], right ); + VectorMA( right, -line[0], cg.refdef.viewaxis[2], right ); + VectorNormalize( right ); + + VectorMA( finish, cg_tracerWidth.value, right, verts[0].xyz ); + verts[0].st[0] = 0; + verts[0].st[1] = 1; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255; + + VectorMA( finish, -cg_tracerWidth.value, right, verts[1].xyz ); + verts[1].st[0] = 1; + verts[1].st[1] = 0; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255; + + VectorMA( start, -cg_tracerWidth.value, right, verts[2].xyz ); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255; + + VectorMA( start, cg_tracerWidth.value, right, verts[3].xyz ); + verts[3].st[0] = 0; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255; + + trap_R_AddPolyToScene( cgs.media.tracerShader, 4, verts ); + + midpoint[0] = ( start[0] + finish[0] ) * 0.5; + midpoint[1] = ( start[1] + finish[1] ) * 0.5; + midpoint[2] = ( start[2] + finish[2] ) * 0.5; + + // add the tracer sound + trap_S_StartSound( midpoint, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.tracerSound ); + +} + + +/* +====================== +CG_CalcMuzzlePoint +====================== +*/ +static qboolean CG_CalcMuzzlePoint( int entityNum, vec3_t muzzle ) { + vec3_t forward; + centity_t *cent; + int anim; + + if ( entityNum == cg.snap->ps.clientNum ) { + VectorCopy( cg.snap->ps.origin, muzzle ); + muzzle[2] += cg.snap->ps.viewheight; + AngleVectors( cg.snap->ps.viewangles, forward, NULL, NULL ); + VectorMA( muzzle, 14, forward, muzzle ); + return qtrue; + } + + cent = &cg_entities[entityNum]; + if ( !cent->currentValid ) { + return qfalse; + } + + VectorCopy( cent->currentState.pos.trBase, muzzle ); + + AngleVectors( cent->currentState.apos.trBase, forward, NULL, NULL ); + anim = cent->currentState.legsAnim & ~ANIM_TOGGLEBIT; + if ( anim == LEGS_WALKCR || anim == LEGS_IDLECR ) { + muzzle[2] += CROUCH_VIEWHEIGHT; + } else { + muzzle[2] += DEFAULT_VIEWHEIGHT; + } + + VectorMA( muzzle, 14, forward, muzzle ); + + return qtrue; + +} + +/* +====================== +CG_Bullet + +Renders bullet effects. +====================== +*/ +void CG_Bullet( vec3_t end, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum ) { + trace_t trace; + int sourceContentType, destContentType; + vec3_t start; + + // if the shooter is currently valid, calc a source point and possibly + // do trail effects + if ( sourceEntityNum >= 0 && cg_tracerChance.value > 0 ) { + if ( CG_CalcMuzzlePoint( sourceEntityNum, start ) ) { + sourceContentType = trap_CM_PointContents( start, 0 ); + destContentType = trap_CM_PointContents( end, 0 ); + + // do a complete bubble trail if necessary + if ( ( sourceContentType == destContentType ) && ( sourceContentType & CONTENTS_WATER ) ) { + CG_BubbleTrail( start, end, 32 ); + } + // bubble trail from water into air + else if ( ( sourceContentType & CONTENTS_WATER ) ) { + trap_CM_BoxTrace( &trace, end, start, NULL, NULL, 0, CONTENTS_WATER ); + CG_BubbleTrail( start, trace.endpos, 32 ); + } + // bubble trail from air into water + else if ( ( destContentType & CONTENTS_WATER ) ) { + trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, CONTENTS_WATER ); + CG_BubbleTrail( trace.endpos, end, 32 ); + } + + // draw a tracer + if ( random() < cg_tracerChance.value ) { + CG_Tracer( start, end ); + } + } + } + + // impact splash and mark + if ( flesh ) { + CG_Bleed( end, fleshEntityNum ); + } else { + CG_MissileHitWall( WP_MACHINEGUN, 0, end, normal, IMPACTSOUND_DEFAULT ); + } + +} diff --git a/reaction/engine/code/client/cl_avi.c b/reaction/engine/code/client/cl_avi.c new file mode 100644 index 00000000..12cde119 --- /dev/null +++ b/reaction/engine/code/client/cl_avi.c @@ -0,0 +1,671 @@ +/* +=========================================================================== +Copyright (C) 2005-2006 Tim Angus + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "client.h" +#include "snd_local.h" + +#define INDEX_FILE_EXTENSION ".index.dat" + +#define MAX_RIFF_CHUNKS 16 + +typedef struct audioFormat_s +{ + int rate; + int format; + int channels; + int bits; + + int sampleSize; + int totalBytes; +} audioFormat_t; + +typedef struct aviFileData_s +{ + qboolean fileOpen; + fileHandle_t f; + char fileName[ MAX_QPATH ]; + int fileSize; + int moviOffset; + int moviSize; + + fileHandle_t idxF; + int numIndices; + + int frameRate; + int framePeriod; + int width, height; + int numVideoFrames; + int maxRecordSize; + qboolean motionJpeg; + + qboolean audio; + audioFormat_t a; + int numAudioFrames; + + int chunkStack[ MAX_RIFF_CHUNKS ]; + int chunkStackTop; + + byte *cBuffer, *eBuffer; +} aviFileData_t; + +static aviFileData_t afd; + +#define MAX_AVI_BUFFER 2048 + +static byte buffer[ MAX_AVI_BUFFER ]; +static int bufIndex; + +/* +=============== +SafeFS_Write +=============== +*/ +static ID_INLINE void SafeFS_Write( const void *buffer, int len, fileHandle_t f ) +{ + if( FS_Write( buffer, len, f ) < len ) + Com_Error( ERR_DROP, "Failed to write avi file\n" ); +} + +/* +=============== +WRITE_STRING +=============== +*/ +static ID_INLINE void WRITE_STRING( const char *s ) +{ + Com_Memcpy( &buffer[ bufIndex ], s, strlen( s ) ); + bufIndex += strlen( s ); +} + +/* +=============== +WRITE_4BYTES +=============== +*/ +static ID_INLINE void WRITE_4BYTES( int x ) +{ + buffer[ bufIndex + 0 ] = (byte)( ( x >> 0 ) & 0xFF ); + buffer[ bufIndex + 1 ] = (byte)( ( x >> 8 ) & 0xFF ); + buffer[ bufIndex + 2 ] = (byte)( ( x >> 16 ) & 0xFF ); + buffer[ bufIndex + 3 ] = (byte)( ( x >> 24 ) & 0xFF ); + bufIndex += 4; +} + +/* +=============== +WRITE_2BYTES +=============== +*/ +static ID_INLINE void WRITE_2BYTES( int x ) +{ + buffer[ bufIndex + 0 ] = (byte)( ( x >> 0 ) & 0xFF ); + buffer[ bufIndex + 1 ] = (byte)( ( x >> 8 ) & 0xFF ); + bufIndex += 2; +} + +/* +=============== +WRITE_1BYTES +=============== +*/ +static ID_INLINE void WRITE_1BYTES( int x ) +{ + buffer[ bufIndex ] = x; + bufIndex += 1; +} + +/* +=============== +START_CHUNK +=============== +*/ +static ID_INLINE void START_CHUNK( const char *s ) +{ + if( afd.chunkStackTop == MAX_RIFF_CHUNKS ) + { + Com_Error( ERR_DROP, "ERROR: Top of chunkstack breached\n" ); + } + + afd.chunkStack[ afd.chunkStackTop ] = bufIndex; + afd.chunkStackTop++; + WRITE_STRING( s ); + WRITE_4BYTES( 0 ); +} + +/* +=============== +END_CHUNK +=============== +*/ +static ID_INLINE void END_CHUNK( void ) +{ + int endIndex = bufIndex; + + if( afd.chunkStackTop <= 0 ) + { + Com_Error( ERR_DROP, "ERROR: Bottom of chunkstack breached\n" ); + } + + afd.chunkStackTop--; + bufIndex = afd.chunkStack[ afd.chunkStackTop ]; + bufIndex += 4; + WRITE_4BYTES( endIndex - bufIndex - 4 ); + bufIndex = endIndex; + bufIndex = PAD( bufIndex, 2 ); +} + +/* +=============== +CL_WriteAVIHeader +=============== +*/ +void CL_WriteAVIHeader( void ) +{ + bufIndex = 0; + afd.chunkStackTop = 0; + + START_CHUNK( "RIFF" ); + { + WRITE_STRING( "AVI " ); + { + START_CHUNK( "LIST" ); + { + WRITE_STRING( "hdrl" ); + WRITE_STRING( "avih" ); + WRITE_4BYTES( 56 ); //"avih" "chunk" size + WRITE_4BYTES( afd.framePeriod ); //dwMicroSecPerFrame + WRITE_4BYTES( afd.maxRecordSize * + afd.frameRate ); //dwMaxBytesPerSec + WRITE_4BYTES( 0 ); //dwReserved1 + WRITE_4BYTES( 0x110 ); //dwFlags bits HAS_INDEX and IS_INTERLEAVED + WRITE_4BYTES( afd.numVideoFrames ); //dwTotalFrames + WRITE_4BYTES( 0 ); //dwInitialFrame + + if( afd.audio ) //dwStreams + WRITE_4BYTES( 2 ); + else + WRITE_4BYTES( 1 ); + + WRITE_4BYTES( afd.maxRecordSize ); //dwSuggestedBufferSize + WRITE_4BYTES( afd.width ); //dwWidth + WRITE_4BYTES( afd.height ); //dwHeight + WRITE_4BYTES( 0 ); //dwReserved[ 0 ] + WRITE_4BYTES( 0 ); //dwReserved[ 1 ] + WRITE_4BYTES( 0 ); //dwReserved[ 2 ] + WRITE_4BYTES( 0 ); //dwReserved[ 3 ] + + START_CHUNK( "LIST" ); + { + WRITE_STRING( "strl" ); + WRITE_STRING( "strh" ); + WRITE_4BYTES( 56 ); //"strh" "chunk" size + WRITE_STRING( "vids" ); + + if( afd.motionJpeg ) + WRITE_STRING( "MJPG" ); + else + WRITE_4BYTES( 0 ); // BI_RGB + + WRITE_4BYTES( 0 ); //dwFlags + WRITE_4BYTES( 0 ); //dwPriority + WRITE_4BYTES( 0 ); //dwInitialFrame + + WRITE_4BYTES( 1 ); //dwTimescale + WRITE_4BYTES( afd.frameRate ); //dwDataRate + WRITE_4BYTES( 0 ); //dwStartTime + WRITE_4BYTES( afd.numVideoFrames ); //dwDataLength + + WRITE_4BYTES( afd.maxRecordSize ); //dwSuggestedBufferSize + WRITE_4BYTES( -1 ); //dwQuality + WRITE_4BYTES( 0 ); //dwSampleSize + WRITE_2BYTES( 0 ); //rcFrame + WRITE_2BYTES( 0 ); //rcFrame + WRITE_2BYTES( afd.width ); //rcFrame + WRITE_2BYTES( afd.height ); //rcFrame + + WRITE_STRING( "strf" ); + WRITE_4BYTES( 40 ); //"strf" "chunk" size + WRITE_4BYTES( 40 ); //biSize + WRITE_4BYTES( afd.width ); //biWidth + WRITE_4BYTES( afd.height ); //biHeight + WRITE_2BYTES( 1 ); //biPlanes + WRITE_2BYTES( 24 ); //biBitCount + + if( afd.motionJpeg ) //biCompression + { + WRITE_STRING( "MJPG" ); + WRITE_4BYTES( afd.width * + afd.height ); //biSizeImage + } + else + { + WRITE_4BYTES( 0 ); // BI_RGB + WRITE_4BYTES( afd.width * + afd.height * 3 ); //biSizeImage + } + + WRITE_4BYTES( 0 ); //biXPelsPetMeter + WRITE_4BYTES( 0 ); //biYPelsPetMeter + WRITE_4BYTES( 0 ); //biClrUsed + WRITE_4BYTES( 0 ); //biClrImportant + } + END_CHUNK( ); + + if( afd.audio ) + { + START_CHUNK( "LIST" ); + { + WRITE_STRING( "strl" ); + WRITE_STRING( "strh" ); + WRITE_4BYTES( 56 ); //"strh" "chunk" size + WRITE_STRING( "auds" ); + WRITE_4BYTES( 0 ); //FCC + WRITE_4BYTES( 0 ); //dwFlags + WRITE_4BYTES( 0 ); //dwPriority + WRITE_4BYTES( 0 ); //dwInitialFrame + + WRITE_4BYTES( afd.a.sampleSize ); //dwTimescale + WRITE_4BYTES( afd.a.sampleSize * + afd.a.rate ); //dwDataRate + WRITE_4BYTES( 0 ); //dwStartTime + WRITE_4BYTES( afd.a.totalBytes / + afd.a.sampleSize ); //dwDataLength + + WRITE_4BYTES( 0 ); //dwSuggestedBufferSize + WRITE_4BYTES( -1 ); //dwQuality + WRITE_4BYTES( afd.a.sampleSize ); //dwSampleSize + WRITE_2BYTES( 0 ); //rcFrame + WRITE_2BYTES( 0 ); //rcFrame + WRITE_2BYTES( 0 ); //rcFrame + WRITE_2BYTES( 0 ); //rcFrame + + WRITE_STRING( "strf" ); + WRITE_4BYTES( 18 ); //"strf" "chunk" size + WRITE_2BYTES( afd.a.format ); //wFormatTag + WRITE_2BYTES( afd.a.channels ); //nChannels + WRITE_4BYTES( afd.a.rate ); //nSamplesPerSec + WRITE_4BYTES( afd.a.sampleSize * + afd.a.rate ); //nAvgBytesPerSec + WRITE_2BYTES( afd.a.sampleSize ); //nBlockAlign + WRITE_2BYTES( afd.a.bits ); //wBitsPerSample + WRITE_2BYTES( 0 ); //cbSize + } + END_CHUNK( ); + } + } + END_CHUNK( ); + + afd.moviOffset = bufIndex; + + START_CHUNK( "LIST" ); + { + WRITE_STRING( "movi" ); + } + } + } +} + +/* +=============== +CL_OpenAVIForWriting + +Creates an AVI file and gets it into a state where +writing the actual data can begin +=============== +*/ +qboolean CL_OpenAVIForWriting( const char *fileName ) +{ + if( afd.fileOpen ) + return qfalse; + + Com_Memset( &afd, 0, sizeof( aviFileData_t ) ); + + // Don't start if a framerate has not been chosen + if( cl_aviFrameRate->integer <= 0 ) + { + Com_Printf( S_COLOR_RED "cl_aviFrameRate must be >= 1\n" ); + return qfalse; + } + + if( ( afd.f = FS_FOpenFileWrite( fileName ) ) <= 0 ) + return qfalse; + + if( ( afd.idxF = FS_FOpenFileWrite( + va( "%s" INDEX_FILE_EXTENSION, fileName ) ) ) <= 0 ) + { + FS_FCloseFile( afd.f ); + return qfalse; + } + + Q_strncpyz( afd.fileName, fileName, MAX_QPATH ); + + afd.frameRate = cl_aviFrameRate->integer; + afd.framePeriod = (int)( 1000000.0f / afd.frameRate ); + afd.width = cls.glconfig.vidWidth; + afd.height = cls.glconfig.vidHeight; + + if( cl_aviMotionJpeg->integer ) + afd.motionJpeg = qtrue; + else + afd.motionJpeg = qfalse; + + afd.cBuffer = Z_Malloc( afd.width * afd.height * 4 ); + afd.eBuffer = Z_Malloc( afd.width * afd.height * 4 ); + + afd.a.rate = dma.speed; + afd.a.format = WAV_FORMAT_PCM; + afd.a.channels = dma.channels; + afd.a.bits = dma.samplebits; + afd.a.sampleSize = ( afd.a.bits / 8 ) * afd.a.channels; + + if( afd.a.rate % afd.frameRate ) + { + int suggestRate = afd.frameRate; + + while( ( afd.a.rate % suggestRate ) && suggestRate >= 1 ) + suggestRate--; + + Com_Printf( S_COLOR_YELLOW "WARNING: cl_aviFrameRate is not a divisor " + "of the audio rate, suggest %d\n", suggestRate ); + } + + if( !Cvar_VariableIntegerValue( "s_initsound" ) ) + { + afd.audio = qfalse; + } + else if( Q_stricmp( Cvar_VariableString( "s_backend" ), "OpenAL" ) ) + { + if( afd.a.bits != 16 || afd.a.channels != 2 ) + { + Com_Printf( S_COLOR_YELLOW "WARNING: Audio format of %d bit/%d channels not supported", + afd.a.bits, afd.a.channels ); + afd.audio = qfalse; + } + else + afd.audio = qtrue; + } + else + { + afd.audio = qfalse; + Com_Printf( S_COLOR_YELLOW "WARNING: Audio capture is not supported " + "with OpenAL. Set s_useOpenAL to 0 for audio capture\n" ); + } + + // This doesn't write a real header, but allocates the + // correct amount of space at the beginning of the file + CL_WriteAVIHeader( ); + + SafeFS_Write( buffer, bufIndex, afd.f ); + afd.fileSize = bufIndex; + + bufIndex = 0; + START_CHUNK( "idx1" ); + SafeFS_Write( buffer, bufIndex, afd.idxF ); + + afd.moviSize = 4; // For the "movi" + afd.fileOpen = qtrue; + + return qtrue; +} + +/* +=============== +CL_CheckFileSize +=============== +*/ +static qboolean CL_CheckFileSize( int bytesToAdd ) +{ + unsigned int newFileSize; + + newFileSize = + afd.fileSize + // Current file size + bytesToAdd + // What we want to add + ( afd.numIndices * 16 ) + // The index + 4; // The index size + + // I assume all the operating systems + // we target can handle a 2Gb file + if( newFileSize > INT_MAX ) + { + // Close the current file... + CL_CloseAVI( ); + + // ...And open a new one + CL_OpenAVIForWriting( va( "%s_", afd.fileName ) ); + + return qtrue; + } + + return qfalse; +} + +/* +=============== +CL_WriteAVIVideoFrame +=============== +*/ +void CL_WriteAVIVideoFrame( const byte *imageBuffer, int size ) +{ + int chunkOffset = afd.fileSize - afd.moviOffset - 8; + int chunkSize = 8 + size; + int paddingSize = PAD( size, 2 ) - size; + byte padding[ 4 ] = { 0 }; + + if( !afd.fileOpen ) + return; + + // Chunk header + contents + padding + if( CL_CheckFileSize( 8 + size + 2 ) ) + return; + + bufIndex = 0; + WRITE_STRING( "00dc" ); + WRITE_4BYTES( size ); + + SafeFS_Write( buffer, 8, afd.f ); + SafeFS_Write( imageBuffer, size, afd.f ); + SafeFS_Write( padding, paddingSize, afd.f ); + afd.fileSize += ( chunkSize + paddingSize ); + + afd.numVideoFrames++; + afd.moviSize += ( chunkSize + paddingSize ); + + if( size > afd.maxRecordSize ) + afd.maxRecordSize = size; + + // Index + bufIndex = 0; + WRITE_STRING( "00dc" ); //dwIdentifier + WRITE_4BYTES( 0x00000010 ); //dwFlags (all frames are KeyFrames) + WRITE_4BYTES( chunkOffset ); //dwOffset + WRITE_4BYTES( size ); //dwLength + SafeFS_Write( buffer, 16, afd.idxF ); + + afd.numIndices++; +} + +#define PCM_BUFFER_SIZE 44100 + +/* +=============== +CL_WriteAVIAudioFrame +=============== +*/ +void CL_WriteAVIAudioFrame( const byte *pcmBuffer, int size ) +{ + static byte pcmCaptureBuffer[ PCM_BUFFER_SIZE ] = { 0 }; + static int bytesInBuffer = 0; + + if( !afd.audio ) + return; + + if( !afd.fileOpen ) + return; + + // Chunk header + contents + padding + if( CL_CheckFileSize( 8 + bytesInBuffer + size + 2 ) ) + return; + + if( bytesInBuffer + size > PCM_BUFFER_SIZE ) + { + Com_Printf( S_COLOR_YELLOW + "WARNING: Audio capture buffer overflow -- truncating\n" ); + size = PCM_BUFFER_SIZE - bytesInBuffer; + } + + Com_Memcpy( &pcmCaptureBuffer[ bytesInBuffer ], pcmBuffer, size ); + bytesInBuffer += size; + + // Only write if we have a frame's worth of audio + if( bytesInBuffer >= (int)ceil( (float)afd.a.rate / (float)afd.frameRate ) * + afd.a.sampleSize ) + { + int chunkOffset = afd.fileSize - afd.moviOffset - 8; + int chunkSize = 8 + bytesInBuffer; + int paddingSize = PAD( bytesInBuffer, 2 ) - bytesInBuffer; + byte padding[ 4 ] = { 0 }; + + bufIndex = 0; + WRITE_STRING( "01wb" ); + WRITE_4BYTES( bytesInBuffer ); + + SafeFS_Write( buffer, 8, afd.f ); + SafeFS_Write( pcmCaptureBuffer, bytesInBuffer, afd.f ); + SafeFS_Write( padding, paddingSize, afd.f ); + afd.fileSize += ( chunkSize + paddingSize ); + + afd.numAudioFrames++; + afd.moviSize += ( chunkSize + paddingSize ); + afd.a.totalBytes =+ bytesInBuffer; + + // Index + bufIndex = 0; + WRITE_STRING( "01wb" ); //dwIdentifier + WRITE_4BYTES( 0 ); //dwFlags + WRITE_4BYTES( chunkOffset ); //dwOffset + WRITE_4BYTES( bytesInBuffer ); //dwLength + SafeFS_Write( buffer, 16, afd.idxF ); + + afd.numIndices++; + + bytesInBuffer = 0; + } +} + +/* +=============== +CL_TakeVideoFrame +=============== +*/ +void CL_TakeVideoFrame( void ) +{ + // AVI file isn't open + if( !afd.fileOpen ) + return; + + re.TakeVideoFrame( afd.width, afd.height, + afd.cBuffer, afd.eBuffer, afd.motionJpeg ); +} + +/* +=============== +CL_CloseAVI + +Closes the AVI file and writes an index chunk +=============== +*/ +qboolean CL_CloseAVI( void ) +{ + int indexRemainder; + int indexSize = afd.numIndices * 16; + const char *idxFileName = va( "%s" INDEX_FILE_EXTENSION, afd.fileName ); + + // AVI file isn't open + if( !afd.fileOpen ) + return qfalse; + + afd.fileOpen = qfalse; + + FS_Seek( afd.idxF, 4, FS_SEEK_SET ); + bufIndex = 0; + WRITE_4BYTES( indexSize ); + SafeFS_Write( buffer, bufIndex, afd.idxF ); + FS_FCloseFile( afd.idxF ); + + // Write index + + // Open the temp index file + if( ( indexSize = FS_FOpenFileRead( idxFileName, + &afd.idxF, qtrue ) ) <= 0 ) + { + FS_FCloseFile( afd.f ); + return qfalse; + } + + indexRemainder = indexSize; + + // Append index to end of avi file + while( indexRemainder > MAX_AVI_BUFFER ) + { + FS_Read( buffer, MAX_AVI_BUFFER, afd.idxF ); + SafeFS_Write( buffer, MAX_AVI_BUFFER, afd.f ); + afd.fileSize += MAX_AVI_BUFFER; + indexRemainder -= MAX_AVI_BUFFER; + } + FS_Read( buffer, indexRemainder, afd.idxF ); + SafeFS_Write( buffer, indexRemainder, afd.f ); + afd.fileSize += indexRemainder; + FS_FCloseFile( afd.idxF ); + + // Remove temp index file + FS_HomeRemove( idxFileName ); + + // Write the real header + FS_Seek( afd.f, 0, FS_SEEK_SET ); + CL_WriteAVIHeader( ); + + bufIndex = 4; + WRITE_4BYTES( afd.fileSize - 8 ); // "RIFF" size + + bufIndex = afd.moviOffset + 4; // Skip "LIST" + WRITE_4BYTES( afd.moviSize ); + + SafeFS_Write( buffer, bufIndex, afd.f ); + + Z_Free( afd.cBuffer ); + Z_Free( afd.eBuffer ); + FS_FCloseFile( afd.f ); + + Com_Printf( "Wrote %d:%d frames to %s\n", afd.numVideoFrames, afd.numAudioFrames, afd.fileName ); + + return qtrue; +} + +/* +=============== +CL_VideoRecording +=============== +*/ +qboolean CL_VideoRecording( void ) +{ + return afd.fileOpen; +} diff --git a/reaction/engine/code/client/cl_cgame.c b/reaction/engine/code/client/cl_cgame.c new file mode 100644 index 00000000..ef15798f --- /dev/null +++ b/reaction/engine/code/client/cl_cgame.c @@ -0,0 +1,1103 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// cl_cgame.c -- client system interaction with client game + +#include "client.h" + +#include "../botlib/botlib.h" + +#include "libmumblelink.h" + +extern botlib_export_t *botlib_export; + +extern qboolean loadCamera(const char *name); +extern void startCamera(int time); +extern qboolean getCameraInfo(int time, vec3_t *origin, vec3_t *angles); + +/* +==================== +CL_GetGameState +==================== +*/ +void CL_GetGameState( gameState_t *gs ) { + *gs = cl.gameState; +} + +/* +==================== +CL_GetGlconfig +==================== +*/ +void CL_GetGlconfig( glconfig_t *glconfig ) { + *glconfig = cls.glconfig; +} + + +/* +==================== +CL_GetUserCmd +==================== +*/ +qboolean CL_GetUserCmd( int cmdNumber, usercmd_t *ucmd ) { + // cmds[cmdNumber] is the last properly generated command + + // can't return anything that we haven't created yet + if ( cmdNumber > cl.cmdNumber ) { + Com_Error( ERR_DROP, "CL_GetUserCmd: %i >= %i", cmdNumber, cl.cmdNumber ); + } + + // the usercmd has been overwritten in the wrapping + // buffer because it is too far out of date + if ( cmdNumber <= cl.cmdNumber - CMD_BACKUP ) { + return qfalse; + } + + *ucmd = cl.cmds[ cmdNumber & CMD_MASK ]; + + return qtrue; +} + +int CL_GetCurrentCmdNumber( void ) { + return cl.cmdNumber; +} + + +/* +==================== +CL_GetParseEntityState +==================== +*/ +qboolean CL_GetParseEntityState( int parseEntityNumber, entityState_t *state ) { + // can't return anything that hasn't been parsed yet + if ( parseEntityNumber >= cl.parseEntitiesNum ) { + Com_Error( ERR_DROP, "CL_GetParseEntityState: %i >= %i", + parseEntityNumber, cl.parseEntitiesNum ); + } + + // can't return anything that has been overwritten in the circular buffer + if ( parseEntityNumber <= cl.parseEntitiesNum - MAX_PARSE_ENTITIES ) { + return qfalse; + } + + *state = cl.parseEntities[ parseEntityNumber & ( MAX_PARSE_ENTITIES - 1 ) ]; + return qtrue; +} + +/* +==================== +CL_GetCurrentSnapshotNumber +==================== +*/ +void CL_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ) { + *snapshotNumber = cl.snap.messageNum; + *serverTime = cl.snap.serverTime; +} + +/* +==================== +CL_GetSnapshot +==================== +*/ +qboolean CL_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) { + clSnapshot_t *clSnap; + int i, count; + + if ( snapshotNumber > cl.snap.messageNum ) { + Com_Error( ERR_DROP, "CL_GetSnapshot: snapshotNumber > cl.snapshot.messageNum" ); + } + + // if the frame has fallen out of the circular buffer, we can't return it + if ( cl.snap.messageNum - snapshotNumber >= PACKET_BACKUP ) { + return qfalse; + } + + // if the frame is not valid, we can't return it + clSnap = &cl.snapshots[snapshotNumber & PACKET_MASK]; + if ( !clSnap->valid ) { + return qfalse; + } + + // if the entities in the frame have fallen out of their + // circular buffer, we can't return it + if ( cl.parseEntitiesNum - clSnap->parseEntitiesNum >= MAX_PARSE_ENTITIES ) { + return qfalse; + } + + // write the snapshot + snapshot->snapFlags = clSnap->snapFlags; + snapshot->serverCommandSequence = clSnap->serverCommandNum; + snapshot->ping = clSnap->ping; + snapshot->serverTime = clSnap->serverTime; + Com_Memcpy( snapshot->areamask, clSnap->areamask, sizeof( snapshot->areamask ) ); + snapshot->ps = clSnap->ps; + count = clSnap->numEntities; + if ( count > MAX_ENTITIES_IN_SNAPSHOT ) { + Com_DPrintf( "CL_GetSnapshot: truncated %i entities to %i\n", count, MAX_ENTITIES_IN_SNAPSHOT ); + count = MAX_ENTITIES_IN_SNAPSHOT; + } + snapshot->numEntities = count; + for ( i = 0 ; i < count ; i++ ) { + snapshot->entities[i] = + cl.parseEntities[ ( clSnap->parseEntitiesNum + i ) & (MAX_PARSE_ENTITIES-1) ]; + } + + // FIXME: configstring changes and server commands!!! + + return qtrue; +} + +/* +===================== +CL_SetUserCmdValue +===================== +*/ +void CL_SetUserCmdValue( int userCmdValue, float sensitivityScale ) { + cl.cgameUserCmdValue = userCmdValue; + cl.cgameSensitivity = sensitivityScale; +} + +/* +===================== +CL_AddCgameCommand +===================== +*/ +void CL_AddCgameCommand( const char *cmdName ) { + Cmd_AddCommand( cmdName, NULL ); +} + +/* +===================== +CL_CgameError +===================== +*/ +void CL_CgameError( const char *string ) { + Com_Error( ERR_DROP, "%s", string ); +} + + +/* +===================== +CL_ConfigstringModified +===================== +*/ +void CL_ConfigstringModified( void ) { + char *old, *s; + int i, index; + char *dup; + gameState_t oldGs; + int len; + + index = atoi( Cmd_Argv(1) ); + if ( index < 0 || index >= MAX_CONFIGSTRINGS ) { + Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); + } + // get everything after "cs " + s = Cmd_ArgsFrom(2); + + old = cl.gameState.stringData + cl.gameState.stringOffsets[ index ]; + if ( !strcmp( old, s ) ) { + return; // unchanged + } + + // build the new gameState_t + oldGs = cl.gameState; + + Com_Memset( &cl.gameState, 0, sizeof( cl.gameState ) ); + + // leave the first 0 for uninitialized strings + cl.gameState.dataCount = 1; + + for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { + if ( i == index ) { + dup = s; + } else { + dup = oldGs.stringData + oldGs.stringOffsets[ i ]; + } + if ( !dup[0] ) { + continue; // leave with the default empty string + } + + len = strlen( dup ); + + if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { + Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); + } + + // append it to the gameState string buffer + cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; + Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, dup, len + 1 ); + cl.gameState.dataCount += len + 1; + } + + if ( index == CS_SYSTEMINFO ) { + // parse serverId and other cvars + CL_SystemInfoChanged(); + } + +} + + +/* +=================== +CL_GetServerCommand + +Set up argc/argv for the given command +=================== +*/ +qboolean CL_GetServerCommand( int serverCommandNumber ) { + char *s; + char *cmd; + static char bigConfigString[BIG_INFO_STRING]; + int argc; + + // if we have irretrievably lost a reliable command, drop the connection + if ( serverCommandNumber <= clc.serverCommandSequence - MAX_RELIABLE_COMMANDS ) { + // when a demo record was started after the client got a whole bunch of + // reliable commands then the client never got those first reliable commands + if ( clc.demoplaying ) + return qfalse; + Com_Error( ERR_DROP, "CL_GetServerCommand: a reliable command was cycled out" ); + return qfalse; + } + + if ( serverCommandNumber > clc.serverCommandSequence ) { + Com_Error( ERR_DROP, "CL_GetServerCommand: requested a command not received" ); + return qfalse; + } + + s = clc.serverCommands[ serverCommandNumber & ( MAX_RELIABLE_COMMANDS - 1 ) ]; + clc.lastExecutedServerCommand = serverCommandNumber; + + Com_DPrintf( "serverCommand: %i : %s\n", serverCommandNumber, s ); + +rescan: + Cmd_TokenizeString( s ); + cmd = Cmd_Argv(0); + argc = Cmd_Argc(); + + if ( !strcmp( cmd, "disconnect" ) ) { + // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=552 + // allow server to indicate why they were disconnected + if ( argc >= 2 ) + Com_Error( ERR_SERVERDISCONNECT, "Server disconnected - %s", Cmd_Argv( 1 ) ); + else + Com_Error( ERR_SERVERDISCONNECT, "Server disconnected\n" ); + } + + if ( !strcmp( cmd, "bcs0" ) ) { + Com_sprintf( bigConfigString, BIG_INFO_STRING, "cs %s \"%s", Cmd_Argv(1), Cmd_Argv(2) ); + return qfalse; + } + + if ( !strcmp( cmd, "bcs1" ) ) { + s = Cmd_Argv(2); + if( strlen(bigConfigString) + strlen(s) >= BIG_INFO_STRING ) { + Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" ); + } + strcat( bigConfigString, s ); + return qfalse; + } + + if ( !strcmp( cmd, "bcs2" ) ) { + s = Cmd_Argv(2); + if( strlen(bigConfigString) + strlen(s) + 1 >= BIG_INFO_STRING ) { + Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" ); + } + strcat( bigConfigString, s ); + strcat( bigConfigString, "\"" ); + s = bigConfigString; + goto rescan; + } + + if ( !strcmp( cmd, "cs" ) ) { + CL_ConfigstringModified(); + // reparse the string, because CL_ConfigstringModified may have done another Cmd_TokenizeString() + Cmd_TokenizeString( s ); + return qtrue; + } + + if ( !strcmp( cmd, "map_restart" ) ) { + // clear notify lines and outgoing commands before passing + // the restart to the cgame + Con_ClearNotify(); + Com_Memset( cl.cmds, 0, sizeof( cl.cmds ) ); + return qtrue; + } + + // the clientLevelShot command is used during development + // to generate 128*128 screenshots from the intermission + // point of levels for the menu system to use + // we pass it along to the cgame to make apropriate adjustments, + // but we also clear the console and notify lines here + if ( !strcmp( cmd, "clientLevelShot" ) ) { + // don't do it if we aren't running the server locally, + // otherwise malicious remote servers could overwrite + // the existing thumbnails + if ( !com_sv_running->integer ) { + return qfalse; + } + // close the console + Con_Close(); + // take a special screenshot next frame + Cbuf_AddText( "wait ; wait ; wait ; wait ; screenshot levelshot\n" ); + return qtrue; + } + + // we may want to put a "connect to other server" command here + + // cgame can now act on the command + return qtrue; +} + + +/* +==================== +CL_CM_LoadMap + +Just adds default parameters that cgame doesn't need to know about +==================== +*/ +void CL_CM_LoadMap( const char *mapname ) { + int checksum; + + CM_LoadMap( mapname, qtrue, &checksum ); +} + +/* +==================== +CL_ShutdonwCGame + +==================== +*/ +void CL_ShutdownCGame( void ) { + Key_SetCatcher( Key_GetCatcher( ) & ~KEYCATCH_CGAME ); + cls.cgameStarted = qfalse; + if ( !cgvm ) { + return; + } + VM_Call( cgvm, CG_SHUTDOWN ); + VM_Free( cgvm ); + cgvm = NULL; +} + +static int FloatAsInt( float f ) { + floatint_t fi; + fi.f = f; + return fi.i; +} + +/* +==================== +CL_CgameSystemCalls + +The cgame module is making a system call +==================== +*/ +intptr_t CL_CgameSystemCalls( intptr_t *args ) { + switch( args[0] ) { + case CG_PRINT: + Com_Printf( "%s", (const char*)VMA(1) ); + return 0; + case CG_ERROR: + Com_Error( ERR_DROP, "%s", (const char*)VMA(1) ); + return 0; + case CG_MILLISECONDS: + return Sys_Milliseconds(); + case CG_CVAR_REGISTER: + Cvar_Register( VMA(1), VMA(2), VMA(3), args[4] ); + return 0; + case CG_CVAR_UPDATE: + Cvar_Update( VMA(1) ); + return 0; + case CG_CVAR_SET: + Cvar_Set( VMA(1), VMA(2) ); + return 0; + case CG_CVAR_VARIABLESTRINGBUFFER: + Cvar_VariableStringBuffer( VMA(1), VMA(2), args[3] ); + return 0; + case CG_ARGC: + return Cmd_Argc(); + case CG_ARGV: + Cmd_ArgvBuffer( args[1], VMA(2), args[3] ); + return 0; + case CG_ARGS: + Cmd_ArgsBuffer( VMA(1), args[2] ); + return 0; + case CG_FS_FOPENFILE: + return FS_FOpenFileByMode( VMA(1), VMA(2), args[3] ); + case CG_FS_READ: + FS_Read2( VMA(1), args[2], args[3] ); + return 0; + case CG_FS_WRITE: + FS_Write( VMA(1), args[2], args[3] ); + return 0; + case CG_FS_FCLOSEFILE: + FS_FCloseFile( args[1] ); + return 0; + case CG_FS_SEEK: + return FS_Seek( args[1], args[2], args[3] ); + case CG_SENDCONSOLECOMMAND: + Cbuf_AddText( VMA(1) ); + return 0; + case CG_ADDCOMMAND: + CL_AddCgameCommand( VMA(1) ); + return 0; + case CG_REMOVECOMMAND: + Cmd_RemoveCommand( VMA(1) ); + return 0; + case CG_SENDCLIENTCOMMAND: + CL_AddReliableCommand( VMA(1) ); + return 0; + case CG_UPDATESCREEN: + // this is used during lengthy level loading, so pump message loop +// Com_EventLoop(); // FIXME: if a server restarts here, BAD THINGS HAPPEN! +// We can't call Com_EventLoop here, a restart will crash and this _does_ happen +// if there is a map change while we are downloading at pk3. +// ZOID + SCR_UpdateScreen(); + return 0; + case CG_CM_LOADMAP: + CL_CM_LoadMap( VMA(1) ); + return 0; + case CG_CM_NUMINLINEMODELS: + return CM_NumInlineModels(); + case CG_CM_INLINEMODEL: + return CM_InlineModel( args[1] ); + case CG_CM_TEMPBOXMODEL: + return CM_TempBoxModel( VMA(1), VMA(2), /*int capsule*/ qfalse ); + case CG_CM_TEMPCAPSULEMODEL: + return CM_TempBoxModel( VMA(1), VMA(2), /*int capsule*/ qtrue ); + case CG_CM_POINTCONTENTS: + return CM_PointContents( VMA(1), args[2] ); + case CG_CM_TRANSFORMEDPOINTCONTENTS: + return CM_TransformedPointContents( VMA(1), args[2], VMA(3), VMA(4) ); + case CG_CM_BOXTRACE: + CM_BoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], /*int capsule*/ qfalse ); + return 0; + case CG_CM_CAPSULETRACE: + CM_BoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], /*int capsule*/ qtrue ); + return 0; + case CG_CM_TRANSFORMEDBOXTRACE: + CM_TransformedBoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], VMA(8), VMA(9), /*int capsule*/ qfalse ); + return 0; + case CG_CM_TRANSFORMEDCAPSULETRACE: + CM_TransformedBoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], VMA(8), VMA(9), /*int capsule*/ qtrue ); + return 0; + case CG_CM_MARKFRAGMENTS: + return re.MarkFragments( args[1], VMA(2), VMA(3), args[4], VMA(5), args[6], VMA(7) ); + case CG_S_STARTSOUND: + S_StartSound( VMA(1), args[2], args[3], args[4] ); + return 0; + case CG_S_STARTLOCALSOUND: + S_StartLocalSound( args[1], args[2] ); + return 0; + case CG_S_CLEARLOOPINGSOUNDS: + S_ClearLoopingSounds(args[1]); + return 0; + case CG_S_ADDLOOPINGSOUND: + S_AddLoopingSound( args[1], VMA(2), VMA(3), args[4] ); + return 0; + case CG_S_ADDREALLOOPINGSOUND: + S_AddRealLoopingSound( args[1], VMA(2), VMA(3), args[4] ); + return 0; + case CG_S_STOPLOOPINGSOUND: + S_StopLoopingSound( args[1] ); + return 0; + case CG_S_UPDATEENTITYPOSITION: + S_UpdateEntityPosition( args[1], VMA(2) ); + return 0; + case CG_S_RESPATIALIZE: + S_Respatialize( args[1], VMA(2), VMA(3), args[4] ); + return 0; + case CG_S_REGISTERSOUND: + return S_RegisterSound( VMA(1), args[2] ); + case CG_S_STARTBACKGROUNDTRACK: + S_StartBackgroundTrack( VMA(1), VMA(2) ); + return 0; + case CG_R_LOADWORLDMAP: + re.LoadWorld( VMA(1) ); + return 0; + case CG_R_REGISTERMODEL: + return re.RegisterModel( VMA(1) ); + case CG_R_REGISTERSKIN: + return re.RegisterSkin( VMA(1) ); + case CG_R_REGISTERSHADER: + return re.RegisterShader( VMA(1) ); + case CG_R_REGISTERSHADERNOMIP: + return re.RegisterShaderNoMip( VMA(1) ); + case CG_R_REGISTERFONT: + re.RegisterFont( VMA(1), args[2], VMA(3)); + case CG_R_CLEARSCENE: + re.ClearScene(); + return 0; + case CG_R_ADDREFENTITYTOSCENE: + re.AddRefEntityToScene( VMA(1) ); + return 0; + case CG_R_ADDPOLYTOSCENE: + re.AddPolyToScene( args[1], args[2], VMA(3), 1 ); + return 0; + case CG_R_ADDPOLYSTOSCENE: + re.AddPolyToScene( args[1], args[2], VMA(3), args[4] ); + return 0; + case CG_R_LIGHTFORPOINT: + return re.LightForPoint( VMA(1), VMA(2), VMA(3), VMA(4) ); + case CG_R_ADDLIGHTTOSCENE: + re.AddLightToScene( VMA(1), VMF(2), VMF(3), VMF(4), VMF(5) ); + return 0; + case CG_R_ADDADDITIVELIGHTTOSCENE: + re.AddAdditiveLightToScene( VMA(1), VMF(2), VMF(3), VMF(4), VMF(5) ); + return 0; + case CG_R_RENDERSCENE: + re.RenderScene( VMA(1) ); + return 0; + case CG_R_SETCOLOR: + re.SetColor( VMA(1) ); + return 0; + case CG_R_DRAWSTRETCHPIC: + re.DrawStretchPic( VMF(1), VMF(2), VMF(3), VMF(4), VMF(5), VMF(6), VMF(7), VMF(8), args[9] ); + return 0; + case CG_R_MODELBOUNDS: + re.ModelBounds( args[1], VMA(2), VMA(3) ); + return 0; + case CG_R_LERPTAG: + return re.LerpTag( VMA(1), args[2], args[3], args[4], VMF(5), VMA(6) ); + case CG_GETGLCONFIG: + CL_GetGlconfig( VMA(1) ); + return 0; + case CG_GETGAMESTATE: + CL_GetGameState( VMA(1) ); + return 0; + case CG_GETCURRENTSNAPSHOTNUMBER: + CL_GetCurrentSnapshotNumber( VMA(1), VMA(2) ); + return 0; + case CG_GETSNAPSHOT: + return CL_GetSnapshot( args[1], VMA(2) ); + case CG_GETSERVERCOMMAND: + return CL_GetServerCommand( args[1] ); + case CG_GETCURRENTCMDNUMBER: + return CL_GetCurrentCmdNumber(); + case CG_GETUSERCMD: + return CL_GetUserCmd( args[1], VMA(2) ); + case CG_SETUSERCMDVALUE: + CL_SetUserCmdValue( args[1], VMF(2) ); + return 0; + case CG_MEMORY_REMAINING: + return Hunk_MemoryRemaining(); + case CG_KEY_ISDOWN: + return Key_IsDown( args[1] ); + case CG_KEY_GETCATCHER: + return Key_GetCatcher(); + case CG_KEY_SETCATCHER: + // Don't allow the cgame module to close the console + Key_SetCatcher( args[1] | ( Key_GetCatcher( ) & KEYCATCH_CONSOLE ) ); + return 0; + case CG_KEY_GETKEY: + return Key_GetKey( VMA(1) ); + + + + case CG_MEMSET: + Com_Memset( VMA(1), args[2], args[3] ); + return 0; + case CG_MEMCPY: + Com_Memcpy( VMA(1), VMA(2), args[3] ); + return 0; + case CG_STRNCPY: + strncpy( VMA(1), VMA(2), args[3] ); + return args[1]; + case CG_SIN: + return FloatAsInt( sin( VMF(1) ) ); + case CG_COS: + return FloatAsInt( cos( VMF(1) ) ); + case CG_ATAN2: + return FloatAsInt( atan2( VMF(1), VMF(2) ) ); + case CG_SQRT: + return FloatAsInt( sqrt( VMF(1) ) ); + case CG_FLOOR: + return FloatAsInt( floor( VMF(1) ) ); + case CG_CEIL: + return FloatAsInt( ceil( VMF(1) ) ); + case CG_ACOS: + return FloatAsInt( Q_acos( VMF(1) ) ); + + case CG_PC_ADD_GLOBAL_DEFINE: + return botlib_export->PC_AddGlobalDefine( VMA(1) ); + case CG_PC_LOAD_SOURCE: + return botlib_export->PC_LoadSourceHandle( VMA(1) ); + case CG_PC_FREE_SOURCE: + return botlib_export->PC_FreeSourceHandle( args[1] ); + case CG_PC_READ_TOKEN: + return botlib_export->PC_ReadTokenHandle( args[1], VMA(2) ); + case CG_PC_SOURCE_FILE_AND_LINE: + return botlib_export->PC_SourceFileAndLine( args[1], VMA(2), VMA(3) ); + + case CG_S_STOPBACKGROUNDTRACK: + S_StopBackgroundTrack(); + return 0; + + case CG_REAL_TIME: + return Com_RealTime( VMA(1) ); + case CG_SNAPVECTOR: + Sys_SnapVector( VMA(1) ); + return 0; + + case CG_CIN_PLAYCINEMATIC: + return CIN_PlayCinematic(VMA(1), args[2], args[3], args[4], args[5], args[6]); + + case CG_CIN_STOPCINEMATIC: + return CIN_StopCinematic(args[1]); + + case CG_CIN_RUNCINEMATIC: + return CIN_RunCinematic(args[1]); + + case CG_CIN_DRAWCINEMATIC: + CIN_DrawCinematic(args[1]); + return 0; + + case CG_CIN_SETEXTENTS: + CIN_SetExtents(args[1], args[2], args[3], args[4], args[5]); + return 0; + + case CG_R_REMAP_SHADER: + re.RemapShader( VMA(1), VMA(2), VMA(3) ); + return 0; + +/* + case CG_LOADCAMERA: + return loadCamera(VMA(1)); + + case CG_STARTCAMERA: + startCamera(args[1]); + return 0; + + case CG_GETCAMERAINFO: + return getCameraInfo(args[1], VMA(2), VMA(3)); +*/ + case CG_GET_ENTITY_TOKEN: + return re.GetEntityToken( VMA(1), args[2] ); + case CG_R_INPVS: + return re.inPVS( VMA(1), VMA(2) ); + + default: + assert(0); + Com_Error( ERR_DROP, "Bad cgame system trap: %ld", (long int) args[0] ); + } + return 0; +} + + +/* +==================== +CL_InitCGame + +Should only be called by CL_StartHunkUsers +==================== +*/ +void CL_InitCGame( void ) { + const char *info; + const char *mapname; + int t1, t2; + vmInterpret_t interpret; + + t1 = Sys_Milliseconds(); + + // put away the console + Con_Close(); + + // find the current mapname + info = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SERVERINFO ]; + mapname = Info_ValueForKey( info, "mapname" ); + Com_sprintf( cl.mapname, sizeof( cl.mapname ), "maps/%s.bsp", mapname ); + + // load the dll or bytecode + if ( cl_connectedToPureServer != 0 ) { + // if sv_pure is set we only allow qvms to be loaded + interpret = VMI_COMPILED; + } + else { + interpret = Cvar_VariableValue( "vm_cgame" ); + } + cgvm = VM_Create( "cgame", CL_CgameSystemCalls, interpret ); + if ( !cgvm ) { + Com_Error( ERR_DROP, "VM_Create on cgame failed" ); + } + cls.state = CA_LOADING; + + // init for this gamestate + // use the lastExecutedServerCommand instead of the serverCommandSequence + // otherwise server commands sent just before a gamestate are dropped + VM_Call( cgvm, CG_INIT, clc.serverMessageSequence, clc.lastExecutedServerCommand, clc.clientNum ); + + // reset any CVAR_CHEAT cvars registered by cgame + if ( !clc.demoplaying && !cl_connectedToCheatServer ) + Cvar_SetCheatState(); + + // we will send a usercmd this frame, which + // will cause the server to send us the first snapshot + cls.state = CA_PRIMED; + + t2 = Sys_Milliseconds(); + + Com_Printf( "CL_InitCGame: %5.2f seconds\n", (t2-t1)/1000.0 ); + + // have the renderer touch all its images, so they are present + // on the card even if the driver does deferred loading + re.EndRegistration(); + + // make sure everything is paged in + if (!Sys_LowPhysicalMemory()) { + Com_TouchMemory(); + } + + // clear anything that got printed + Con_ClearNotify (); +} + + +/* +==================== +CL_GameCommand + +See if the current console command is claimed by the cgame +==================== +*/ +qboolean CL_GameCommand( void ) { + if ( !cgvm ) { + return qfalse; + } + + return VM_Call( cgvm, CG_CONSOLE_COMMAND ); +} + + + +/* +===================== +CL_CGameRendering +===================== +*/ +void CL_CGameRendering( stereoFrame_t stereo ) { + VM_Call( cgvm, CG_DRAW_ACTIVE_FRAME, cl.serverTime, stereo, clc.demoplaying ); + VM_Debug( 0 ); +} + + +/* +================= +CL_AdjustTimeDelta + +Adjust the clients view of server time. + +We attempt to have cl.serverTime exactly equal the server's view +of time plus the timeNudge, but with variable latencies over +the internet it will often need to drift a bit to match conditions. + +Our ideal time would be to have the adjusted time approach, but not pass, +the very latest snapshot. + +Adjustments are only made when a new snapshot arrives with a rational +latency, which keeps the adjustment process framerate independent and +prevents massive overadjustment during times of significant packet loss +or bursted delayed packets. +================= +*/ + +#define RESET_TIME 500 + +void CL_AdjustTimeDelta( void ) { + int resetTime; + int newDelta; + int deltaDelta; + + cl.newSnapshots = qfalse; + + // the delta never drifts when replaying a demo + if ( clc.demoplaying ) { + return; + } + + // if the current time is WAY off, just correct to the current value + if ( com_sv_running->integer ) { + resetTime = 100; + } else { + resetTime = RESET_TIME; + } + + newDelta = cl.snap.serverTime - cls.realtime; + deltaDelta = abs( newDelta - cl.serverTimeDelta ); + + if ( deltaDelta > RESET_TIME ) { + cl.serverTimeDelta = newDelta; + cl.oldServerTime = cl.snap.serverTime; // FIXME: is this a problem for cgame? + cl.serverTime = cl.snap.serverTime; + if ( cl_showTimeDelta->integer ) { + Com_Printf( " " ); + } + } else if ( deltaDelta > 100 ) { + // fast adjust, cut the difference in half + if ( cl_showTimeDelta->integer ) { + Com_Printf( " " ); + } + cl.serverTimeDelta = ( cl.serverTimeDelta + newDelta ) >> 1; + } else { + // slow drift adjust, only move 1 or 2 msec + + // if any of the frames between this and the previous snapshot + // had to be extrapolated, nudge our sense of time back a little + // the granularity of +1 / -2 is too high for timescale modified frametimes + if ( com_timescale->value == 0 || com_timescale->value == 1 ) { + if ( cl.extrapolatedSnapshot ) { + cl.extrapolatedSnapshot = qfalse; + cl.serverTimeDelta -= 2; + } else { + // otherwise, move our sense of time forward to minimize total latency + cl.serverTimeDelta++; + } + } + } + + if ( cl_showTimeDelta->integer ) { + Com_Printf( "%i ", cl.serverTimeDelta ); + } +} + + +/* +================== +CL_FirstSnapshot +================== +*/ +void CL_FirstSnapshot( void ) { + // ignore snapshots that don't have entities + if ( cl.snap.snapFlags & SNAPFLAG_NOT_ACTIVE ) { + return; + } + cls.state = CA_ACTIVE; + + // set the timedelta so we are exactly on this first frame + cl.serverTimeDelta = cl.snap.serverTime - cls.realtime; + cl.oldServerTime = cl.snap.serverTime; + + clc.timeDemoBaseTime = cl.snap.serverTime; + + // if this is the first frame of active play, + // execute the contents of activeAction now + // this is to allow scripting a timedemo to start right + // after loading + if ( cl_activeAction->string[0] ) { + Cbuf_AddText( cl_activeAction->string ); + Cvar_Set( "activeAction", "" ); + } + +#ifdef USE_MUMBLE + if ((cl_useMumble->integer) && !mumble_islinked()) { + int ret = mumble_link(CLIENT_WINDOW_TITLE); + Com_Printf("Mumble: Linking to Mumble application %s\n", ret==0?"ok":"failed"); + } +#endif + +#ifdef USE_VOIP + if (!clc.speexInitialized) { + int i; + speex_bits_init(&clc.speexEncoderBits); + speex_bits_reset(&clc.speexEncoderBits); + + clc.speexEncoder = speex_encoder_init(&speex_nb_mode); + + speex_encoder_ctl(clc.speexEncoder, SPEEX_GET_FRAME_SIZE, + &clc.speexFrameSize); + speex_encoder_ctl(clc.speexEncoder, SPEEX_GET_SAMPLING_RATE, + &clc.speexSampleRate); + + clc.speexPreprocessor = speex_preprocess_state_init(clc.speexFrameSize, + clc.speexSampleRate); + + i = 1; + speex_preprocess_ctl(clc.speexPreprocessor, + SPEEX_PREPROCESS_SET_DENOISE, &i); + + i = 1; + speex_preprocess_ctl(clc.speexPreprocessor, + SPEEX_PREPROCESS_SET_AGC, &i); + + for (i = 0; i < MAX_CLIENTS; i++) { + speex_bits_init(&clc.speexDecoderBits[i]); + speex_bits_reset(&clc.speexDecoderBits[i]); + clc.speexDecoder[i] = speex_decoder_init(&speex_nb_mode); + clc.voipIgnore[i] = qfalse; + clc.voipGain[i] = 1.0f; + } + clc.speexInitialized = qtrue; + clc.voipMuteAll = qfalse; + Cmd_AddCommand ("voip", CL_Voip_f); + Cvar_Set("cl_voipSendTarget", "all"); + clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0x7FFFFFFF; + } +#endif +} + +/* +================== +CL_SetCGameTime +================== +*/ +void CL_SetCGameTime( void ) { + // getting a valid frame message ends the connection process + if ( cls.state != CA_ACTIVE ) { + if ( cls.state != CA_PRIMED ) { + return; + } + if ( clc.demoplaying ) { + // we shouldn't get the first snapshot on the same frame + // as the gamestate, because it causes a bad time skip + if ( !clc.firstDemoFrameSkipped ) { + clc.firstDemoFrameSkipped = qtrue; + return; + } + CL_ReadDemoMessage(); + } + if ( cl.newSnapshots ) { + cl.newSnapshots = qfalse; + CL_FirstSnapshot(); + } + if ( cls.state != CA_ACTIVE ) { + return; + } + } + + // if we have gotten to this point, cl.snap is guaranteed to be valid + if ( !cl.snap.valid ) { + Com_Error( ERR_DROP, "CL_SetCGameTime: !cl.snap.valid" ); + } + + // allow pause in single player + if ( sv_paused->integer && CL_CheckPaused() && com_sv_running->integer ) { + // paused + return; + } + + if ( cl.snap.serverTime < cl.oldFrameServerTime ) { + Com_Error( ERR_DROP, "cl.snap.serverTime < cl.oldFrameServerTime" ); + } + cl.oldFrameServerTime = cl.snap.serverTime; + + + // get our current view of time + + if ( clc.demoplaying && cl_freezeDemo->integer ) { + // cl_freezeDemo is used to lock a demo in place for single frame advances + + } else { + // cl_timeNudge is a user adjustable cvar that allows more + // or less latency to be added in the interest of better + // smoothness or better responsiveness. + int tn; + + tn = cl_timeNudge->integer; + if (tn<-30) { + tn = -30; + } else if (tn>30) { + tn = 30; + } + + cl.serverTime = cls.realtime + cl.serverTimeDelta - tn; + + // guarantee that time will never flow backwards, even if + // serverTimeDelta made an adjustment or cl_timeNudge was changed + if ( cl.serverTime < cl.oldServerTime ) { + cl.serverTime = cl.oldServerTime; + } + cl.oldServerTime = cl.serverTime; + + // note if we are almost past the latest frame (without timeNudge), + // so we will try and adjust back a bit when the next snapshot arrives + if ( cls.realtime + cl.serverTimeDelta >= cl.snap.serverTime - 5 ) { + cl.extrapolatedSnapshot = qtrue; + } + } + + // if we have gotten new snapshots, drift serverTimeDelta + // don't do this every frame, or a period of packet loss would + // make a huge adjustment + if ( cl.newSnapshots ) { + CL_AdjustTimeDelta(); + } + + if ( !clc.demoplaying ) { + return; + } + + // if we are playing a demo back, we can just keep reading + // messages from the demo file until the cgame definately + // has valid snapshots to interpolate between + + // a timedemo will always use a deterministic set of time samples + // no matter what speed machine it is run on, + // while a normal demo may have different time samples + // each time it is played back + if ( cl_timedemo->integer ) { + int now = Sys_Milliseconds( ); + int frameDuration; + + if (!clc.timeDemoStart) { + clc.timeDemoStart = clc.timeDemoLastFrame = now; + clc.timeDemoMinDuration = INT_MAX; + clc.timeDemoMaxDuration = 0; + } + + frameDuration = now - clc.timeDemoLastFrame; + clc.timeDemoLastFrame = now; + + // Ignore the first measurement as it'll always be 0 + if( clc.timeDemoFrames > 0 ) + { + if( frameDuration > clc.timeDemoMaxDuration ) + clc.timeDemoMaxDuration = frameDuration; + + if( frameDuration < clc.timeDemoMinDuration ) + clc.timeDemoMinDuration = frameDuration; + + // 255 ms = about 4fps + if( frameDuration > UCHAR_MAX ) + frameDuration = UCHAR_MAX; + + clc.timeDemoDurations[ ( clc.timeDemoFrames - 1 ) % + MAX_TIMEDEMO_DURATIONS ] = frameDuration; + } + + clc.timeDemoFrames++; + cl.serverTime = clc.timeDemoBaseTime + clc.timeDemoFrames * 50; + } + + while ( cl.serverTime >= cl.snap.serverTime ) { + // feed another messag, which should change + // the contents of cl.snap + CL_ReadDemoMessage(); + if ( cls.state != CA_ACTIVE ) { + return; // end of demo + } + } + +} + + + diff --git a/reaction/engine/code/client/cl_cin.c b/reaction/engine/code/client/cl_cin.c new file mode 100644 index 00000000..1fc55208 --- /dev/null +++ b/reaction/engine/code/client/cl_cin.c @@ -0,0 +1,1668 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: cl_cin.c + * + * desc: video and cinematic playback + * + * $Archive: /MissionPack/code/client/cl_cin.c $ + * + * cl_glconfig.hwtype trtypes 3dfx/ragepro need 256x256 + * + *****************************************************************************/ + +#include "client.h" +#include "snd_local.h" + +#define MAXSIZE 8 +#define MINSIZE 4 + +#define DEFAULT_CIN_WIDTH 512 +#define DEFAULT_CIN_HEIGHT 512 + +#define ROQ_QUAD 0x1000 +#define ROQ_QUAD_INFO 0x1001 +#define ROQ_CODEBOOK 0x1002 +#define ROQ_QUAD_VQ 0x1011 +#define ROQ_QUAD_JPEG 0x1012 +#define ROQ_QUAD_HANG 0x1013 +#define ROQ_PACKET 0x1030 +#define ZA_SOUND_MONO 0x1020 +#define ZA_SOUND_STEREO 0x1021 + +#define MAX_VIDEO_HANDLES 16 + +extern glconfig_t glConfig; + + +static void RoQ_init( void ); + +/****************************************************************************** +* +* Class: trFMV +* +* Description: RoQ/RnR manipulation routines +* not entirely complete for first run +* +******************************************************************************/ + +static long ROQ_YY_tab[256]; +static long ROQ_UB_tab[256]; +static long ROQ_UG_tab[256]; +static long ROQ_VG_tab[256]; +static long ROQ_VR_tab[256]; +static unsigned short vq2[256*16*4]; +static unsigned short vq4[256*64*4]; +static unsigned short vq8[256*256*4]; + + +typedef struct { + byte linbuf[DEFAULT_CIN_WIDTH*DEFAULT_CIN_HEIGHT*4*2]; + byte file[65536]; + short sqrTable[256]; + + int mcomp[256]; + byte *qStatus[2][32768]; + + long oldXOff, oldYOff, oldysize, oldxsize; + + int currentHandle; +} cinematics_t; + +typedef struct { + char fileName[MAX_OSPATH]; + int CIN_WIDTH, CIN_HEIGHT; + int xpos, ypos, width, height; + qboolean looping, holdAtEnd, dirty, alterGameState, silent, shader; + fileHandle_t iFile; + e_status status; + unsigned int startTime; + unsigned int lastTime; + long tfps; + long RoQPlayed; + long ROQSize; + unsigned int RoQFrameSize; + long onQuad; + long numQuads; + long samplesPerLine; + unsigned int roq_id; + long screenDelta; + + void ( *VQ0)(byte *status, void *qdata ); + void ( *VQ1)(byte *status, void *qdata ); + void ( *VQNormal)(byte *status, void *qdata ); + void ( *VQBuffer)(byte *status, void *qdata ); + + long samplesPerPixel; // defaults to 2 + byte* gray; + unsigned int xsize, ysize, maxsize, minsize; + + qboolean half, smootheddouble, inMemory; + long normalBuffer0; + long roq_flags; + long roqF0; + long roqF1; + long t[2]; + long roqFPS; + int playonwalls; + byte* buf; + long drawX, drawY; +} cin_cache; + +static cinematics_t cin; +static cin_cache cinTable[MAX_VIDEO_HANDLES]; +static int currentHandle = -1; +static int CL_handle = -1; + +extern int s_soundtime; // sample PAIRS + + +void CIN_CloseAllVideos(void) { + int i; + + for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) { + if (cinTable[i].fileName[0] != 0 ) { + CIN_StopCinematic(i); + } + } +} + + +static int CIN_HandleForVideo(void) { + int i; + + for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) { + if ( cinTable[i].fileName[0] == 0 ) { + return i; + } + } + Com_Error( ERR_DROP, "CIN_HandleForVideo: none free" ); + return -1; +} + + +extern int CL_ScaledMilliseconds(void); + +//----------------------------------------------------------------------------- +// RllSetupTable +// +// Allocates and initializes the square table. +// +// Parameters: None +// +// Returns: Nothing +//----------------------------------------------------------------------------- +static void RllSetupTable( void ) +{ + int z; + + for (z=0;z<128;z++) { + cin.sqrTable[z] = (short)(z*z); + cin.sqrTable[z+128] = (short)(-cin.sqrTable[z]); + } +} + + + +//----------------------------------------------------------------------------- +// RllDecodeMonoToMono +// +// Decode mono source data into a mono buffer. +// +// Parameters: from -> buffer holding encoded data +// to -> buffer to hold decoded data +// size = number of bytes of input (= # of shorts of output) +// signedOutput = 0 for unsigned output, non-zero for signed output +// flag = flags from asset header +// +// Returns: Number of samples placed in output buffer +//----------------------------------------------------------------------------- +long RllDecodeMonoToMono(unsigned char *from,short *to,unsigned int size,char signedOutput ,unsigned short flag) +{ + unsigned int z; + int prev; + + if (signedOutput) + prev = flag - 0x8000; + else + prev = flag; + + for (z=0;z buffer holding encoded data +// to -> buffer to hold decoded data +// size = number of bytes of input (= 1/4 # of bytes of output) +// signedOutput = 0 for unsigned output, non-zero for signed output +// flag = flags from asset header +// +// Returns: Number of samples placed in output buffer +//----------------------------------------------------------------------------- +long RllDecodeMonoToStereo(unsigned char *from,short *to,unsigned int size,char signedOutput,unsigned short flag) +{ + unsigned int z; + int prev; + + if (signedOutput) + prev = flag - 0x8000; + else + prev = flag; + + for (z = 0; z < size; z++) { + prev = (short)(prev + cin.sqrTable[from[z]]); + to[z*2+0] = to[z*2+1] = (short)(prev); + } + + return size; // * 2 * sizeof(short)); +} + + +//----------------------------------------------------------------------------- +// RllDecodeStereoToStereo +// +// Decode stereo source data into a stereo buffer. +// +// Parameters: from -> buffer holding encoded data +// to -> buffer to hold decoded data +// size = number of bytes of input (= 1/2 # of bytes of output) +// signedOutput = 0 for unsigned output, non-zero for signed output +// flag = flags from asset header +// +// Returns: Number of samples placed in output buffer +//----------------------------------------------------------------------------- +long RllDecodeStereoToStereo(unsigned char *from,short *to,unsigned int size,char signedOutput, unsigned short flag) +{ + unsigned int z; + unsigned char *zz = from; + int prevL, prevR; + + if (signedOutput) { + prevL = (flag & 0xff00) - 0x8000; + prevR = ((flag & 0x00ff) << 8) - 0x8000; + } else { + prevL = flag & 0xff00; + prevR = (flag & 0x00ff) << 8; + } + + for (z=0;z>1); //*sizeof(short)); +} + + +//----------------------------------------------------------------------------- +// RllDecodeStereoToMono +// +// Decode stereo source data into a mono buffer. +// +// Parameters: from -> buffer holding encoded data +// to -> buffer to hold decoded data +// size = number of bytes of input (= # of bytes of output) +// signedOutput = 0 for unsigned output, non-zero for signed output +// flag = flags from asset header +// +// Returns: Number of samples placed in output buffer +//----------------------------------------------------------------------------- +long RllDecodeStereoToMono(unsigned char *from,short *to,unsigned int size,char signedOutput, unsigned short flag) +{ + unsigned int z; + int prevL,prevR; + + if (signedOutput) { + prevL = (flag & 0xff00) - 0x8000; + prevR = ((flag & 0x00ff) << 8) -0x8000; + } else { + prevL = flag & 0xff00; + prevR = (flag & 0x00ff) << 8; + } + + for (z=0;z> 2) ); + } +} + +#define VQ2TO4(a,b,c,d) { \ + *c++ = a[0]; \ + *d++ = a[0]; \ + *d++ = a[0]; \ + *c++ = a[1]; \ + *d++ = a[1]; \ + *d++ = a[1]; \ + *c++ = b[0]; \ + *d++ = b[0]; \ + *d++ = b[0]; \ + *c++ = b[1]; \ + *d++ = b[1]; \ + *d++ = b[1]; \ + *d++ = a[0]; \ + *d++ = a[0]; \ + *d++ = a[1]; \ + *d++ = a[1]; \ + *d++ = b[0]; \ + *d++ = b[0]; \ + *d++ = b[1]; \ + *d++ = b[1]; \ + a += 2; b += 2; } + +#define VQ2TO2(a,b,c,d) { \ + *c++ = *a; \ + *d++ = *a; \ + *d++ = *a; \ + *c++ = *b; \ + *d++ = *b; \ + *d++ = *b; \ + *d++ = *a; \ + *d++ = *a; \ + *d++ = *b; \ + *d++ = *b; \ + a++; b++; } + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static unsigned short yuv_to_rgb( long y, long u, long v ) +{ + long r,g,b,YY = (long)(ROQ_YY_tab[(y)]); + + r = (YY + ROQ_VR_tab[v]) >> 9; + g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 8; + b = (YY + ROQ_UB_tab[u]) >> 9; + + if (r<0) r = 0; if (g<0) g = 0; if (b<0) b = 0; + if (r > 31) r = 31; if (g > 63) g = 63; if (b > 31) b = 31; + + return (unsigned short)((r<<11)+(g<<5)+(b)); +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ +static unsigned int yuv_to_rgb24( long y, long u, long v ) +{ + long r,g,b,YY = (long)(ROQ_YY_tab[(y)]); + + r = (YY + ROQ_VR_tab[v]) >> 6; + g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 6; + b = (YY + ROQ_UB_tab[u]) >> 6; + + if (r<0) r = 0; if (g<0) g = 0; if (b<0) b = 0; + if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; + + return LittleLong ((r)|(g<<8)|(b<<16)|(255<<24)); +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void decodeCodeBook( byte *input, unsigned short roq_flags ) +{ + long i, j, two, four; + unsigned short *aptr, *bptr, *cptr, *dptr; + long y0,y1,y2,y3,cr,cb; + byte *bbptr, *baptr, *bcptr, *bdptr; + union { + unsigned int *i; + unsigned short *s; + } iaptr, ibptr, icptr, idptr; + + if (!roq_flags) { + two = four = 256; + } else { + two = roq_flags>>8; + if (!two) two = 256; + four = roq_flags&0xff; + } + + four *= 2; + + bptr = (unsigned short *)vq2; + + if (!cinTable[currentHandle].half) { + if (!cinTable[currentHandle].smootheddouble) { +// +// normal height +// + if (cinTable[currentHandle].samplesPerPixel==2) { + for(i=0;i cinTable[currentHandle].CIN_WIDTH) bigx = cinTable[currentHandle].CIN_WIDTH; + if (bigy > cinTable[currentHandle].CIN_HEIGHT) bigy = cinTable[currentHandle].CIN_HEIGHT; + + if ( (startX >= lowx) && (startX+quadSize) <= (bigx) && (startY+quadSize) <= (bigy) && (startY >= lowy) && quadSize <= MAXSIZE) { + useY = startY; + scroff = cin.linbuf + (useY+((cinTable[currentHandle].CIN_HEIGHT-bigy)>>1)+yOff)*(cinTable[currentHandle].samplesPerLine) + (((startX+xOff))*cinTable[currentHandle].samplesPerPixel); + + cin.qStatus[0][cinTable[currentHandle].onQuad ] = scroff; + cin.qStatus[1][cinTable[currentHandle].onQuad++] = scroff+offset; + } + + if ( quadSize != MINSIZE ) { + quadSize >>= 1; + recurseQuad( startX, startY , quadSize, xOff, yOff ); + recurseQuad( startX+quadSize, startY , quadSize, xOff, yOff ); + recurseQuad( startX, startY+quadSize , quadSize, xOff, yOff ); + recurseQuad( startX+quadSize, startY+quadSize , quadSize, xOff, yOff ); + } +} + + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void setupQuad( long xOff, long yOff ) +{ + long numQuadCels, i,x,y; + byte *temp; + + if (xOff == cin.oldXOff && yOff == cin.oldYOff && cinTable[currentHandle].ysize == cin.oldysize && cinTable[currentHandle].xsize == cin.oldxsize) { + return; + } + + cin.oldXOff = xOff; + cin.oldYOff = yOff; + cin.oldysize = cinTable[currentHandle].ysize; + cin.oldxsize = cinTable[currentHandle].xsize; + + numQuadCels = (cinTable[currentHandle].CIN_WIDTH*cinTable[currentHandle].CIN_HEIGHT) / (16); + numQuadCels += numQuadCels/4 + numQuadCels/16; + numQuadCels += 64; // for overflow + + numQuadCels = (cinTable[currentHandle].xsize*cinTable[currentHandle].ysize) / (16); + numQuadCels += numQuadCels/4; + numQuadCels += 64; // for overflow + + cinTable[currentHandle].onQuad = 0; + + for(y=0;y<(long)cinTable[currentHandle].ysize;y+=16) + for(x=0;x<(long)cinTable[currentHandle].xsize;x+=16) + recurseQuad( x, y, 16, xOff, yOff ); + + temp = NULL; + + for(i=(numQuadCels-64);i256) { + cinTable[currentHandle].drawX = 256; + } + if (cinTable[currentHandle].drawY>256) { + cinTable[currentHandle].drawY = 256; + } + if (cinTable[currentHandle].CIN_WIDTH != 256 || cinTable[currentHandle].CIN_HEIGHT != 256) { + Com_Printf("HACK: approxmimating cinematic for Rage Pro or Voodoo\n"); + } + } +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void RoQPrepMcomp( long xoff, long yoff ) +{ + long i, j, x, y, temp, temp2; + + i=cinTable[currentHandle].samplesPerLine; j=cinTable[currentHandle].samplesPerPixel; + if ( cinTable[currentHandle].xsize == (cinTable[currentHandle].ysize*4) && !cinTable[currentHandle].half ) { j = j+j; i = i+i; } + + for(y=0;y<16;y++) { + temp2 = (y+yoff-8)*i; + for(x=0;x<16;x++) { + temp = (x+xoff-8)*j; + cin.mcomp[(x*16)+y] = cinTable[currentHandle].normalBuffer0-(temp2+temp); + } + } +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void initRoQ( void ) +{ + if (currentHandle < 0) return; + + cinTable[currentHandle].VQNormal = (void (*)(byte *, void *))blitVQQuad32fs; + cinTable[currentHandle].VQBuffer = (void (*)(byte *, void *))blitVQQuad32fs; + cinTable[currentHandle].samplesPerPixel = 4; + ROQ_GenYUVTables(); + RllSetupTable(); +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ +/* +static byte* RoQFetchInterlaced( byte *source ) { + int x, *src, *dst; + + if (currentHandle < 0) return NULL; + + src = (int *)source; + dst = (int *)cinTable[currentHandle].buf2; + + for(x=0;x<256*256;x++) { + *dst = *src; + dst++; src += 2; + } + return cinTable[currentHandle].buf2; +} +*/ +static void RoQReset( void ) { + + if (currentHandle < 0) return; + + FS_FCloseFile( cinTable[currentHandle].iFile ); + FS_FOpenFileRead (cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, qtrue); + // let the background thread start reading ahead + FS_Read (cin.file, 16, cinTable[currentHandle].iFile); + RoQ_init(); + cinTable[currentHandle].status = FMV_LOOPED; +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void RoQInterrupt(void) +{ + byte *framedata; + short sbuf[32768]; + int ssize; + + if (currentHandle < 0) return; + + FS_Read( cin.file, cinTable[currentHandle].RoQFrameSize+8, cinTable[currentHandle].iFile ); + if ( cinTable[currentHandle].RoQPlayed >= cinTable[currentHandle].ROQSize ) { + if (cinTable[currentHandle].holdAtEnd==qfalse) { + if (cinTable[currentHandle].looping) { + RoQReset(); + } else { + cinTable[currentHandle].status = FMV_EOF; + } + } else { + cinTable[currentHandle].status = FMV_IDLE; + } + return; + } + + framedata = cin.file; +// +// new frame is ready +// +redump: + switch(cinTable[currentHandle].roq_id) + { + case ROQ_QUAD_VQ: + if ((cinTable[currentHandle].numQuads&1)) { + cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[1]; + RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 ); + cinTable[currentHandle].VQ1( (byte *)cin.qStatus[1], framedata); + cinTable[currentHandle].buf = cin.linbuf + cinTable[currentHandle].screenDelta; + } else { + cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[0]; + RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 ); + cinTable[currentHandle].VQ0( (byte *)cin.qStatus[0], framedata ); + cinTable[currentHandle].buf = cin.linbuf; + } + if (cinTable[currentHandle].numQuads == 0) { // first frame + Com_Memcpy(cin.linbuf+cinTable[currentHandle].screenDelta, cin.linbuf, cinTable[currentHandle].samplesPerLine*cinTable[currentHandle].ysize); + } + cinTable[currentHandle].numQuads++; + cinTable[currentHandle].dirty = qtrue; + break; + case ROQ_CODEBOOK: + decodeCodeBook( framedata, (unsigned short)cinTable[currentHandle].roq_flags ); + break; + case ZA_SOUND_MONO: + if (!cinTable[currentHandle].silent) { + ssize = RllDecodeMonoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags); + S_RawSamples( 0, ssize, 22050, 2, 1, (byte *)sbuf, 1.0f ); + } + break; + case ZA_SOUND_STEREO: + if (!cinTable[currentHandle].silent) { + if (cinTable[currentHandle].numQuads == -1) { + S_Update(); + s_rawend[0] = s_soundtime; + } + ssize = RllDecodeStereoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags); + S_RawSamples( 0, ssize, 22050, 2, 2, (byte *)sbuf, 1.0f ); + } + break; + case ROQ_QUAD_INFO: + if (cinTable[currentHandle].numQuads == -1) { + readQuadInfo( framedata ); + setupQuad( 0, 0 ); + // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer + cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = CL_ScaledMilliseconds()*com_timescale->value; + } + if (cinTable[currentHandle].numQuads != 1) cinTable[currentHandle].numQuads = 0; + break; + case ROQ_PACKET: + cinTable[currentHandle].inMemory = cinTable[currentHandle].roq_flags; + cinTable[currentHandle].RoQFrameSize = 0; // for header + break; + case ROQ_QUAD_HANG: + cinTable[currentHandle].RoQFrameSize = 0; + break; + case ROQ_QUAD_JPEG: + break; + default: + cinTable[currentHandle].status = FMV_EOF; + break; + } +// +// read in next frame data +// + if ( cinTable[currentHandle].RoQPlayed >= cinTable[currentHandle].ROQSize ) { + if (cinTable[currentHandle].holdAtEnd==qfalse) { + if (cinTable[currentHandle].looping) { + RoQReset(); + } else { + cinTable[currentHandle].status = FMV_EOF; + } + } else { + cinTable[currentHandle].status = FMV_IDLE; + } + return; + } + + framedata += cinTable[currentHandle].RoQFrameSize; + cinTable[currentHandle].roq_id = framedata[0] + framedata[1]*256; + cinTable[currentHandle].RoQFrameSize = framedata[2] + framedata[3]*256 + framedata[4]*65536; + cinTable[currentHandle].roq_flags = framedata[6] + framedata[7]*256; + cinTable[currentHandle].roqF0 = (signed char)framedata[7]; + cinTable[currentHandle].roqF1 = (signed char)framedata[6]; + + if (cinTable[currentHandle].RoQFrameSize>65536||cinTable[currentHandle].roq_id==0x1084) { + Com_DPrintf("roq_size>65536||roq_id==0x1084\n"); + cinTable[currentHandle].status = FMV_EOF; + if (cinTable[currentHandle].looping) { + RoQReset(); + } + return; + } + if (cinTable[currentHandle].inMemory && (cinTable[currentHandle].status != FMV_EOF)) { cinTable[currentHandle].inMemory--; framedata += 8; goto redump; } +// +// one more frame hits the dust +// +// assert(cinTable[currentHandle].RoQFrameSize <= 65536); +// r = FS_Read( cin.file, cinTable[currentHandle].RoQFrameSize+8, cinTable[currentHandle].iFile ); + cinTable[currentHandle].RoQPlayed += cinTable[currentHandle].RoQFrameSize+8; +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void RoQ_init( void ) +{ + // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer + cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = CL_ScaledMilliseconds()*com_timescale->value; + + cinTable[currentHandle].RoQPlayed = 24; + +/* get frame rate */ + cinTable[currentHandle].roqFPS = cin.file[ 6] + cin.file[ 7]*256; + + if (!cinTable[currentHandle].roqFPS) cinTable[currentHandle].roqFPS = 30; + + cinTable[currentHandle].numQuads = -1; + + cinTable[currentHandle].roq_id = cin.file[ 8] + cin.file[ 9]*256; + cinTable[currentHandle].RoQFrameSize = cin.file[10] + cin.file[11]*256 + cin.file[12]*65536; + cinTable[currentHandle].roq_flags = cin.file[14] + cin.file[15]*256; + + if (cinTable[currentHandle].RoQFrameSize > 65536 || !cinTable[currentHandle].RoQFrameSize) { + return; + } + +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void RoQShutdown( void ) { + const char *s; + + if (!cinTable[currentHandle].buf) { + return; + } + + if ( cinTable[currentHandle].status == FMV_IDLE ) { + return; + } + Com_DPrintf("finished cinematic\n"); + cinTable[currentHandle].status = FMV_IDLE; + + if (cinTable[currentHandle].iFile) { + FS_FCloseFile( cinTable[currentHandle].iFile ); + cinTable[currentHandle].iFile = 0; + } + + if (cinTable[currentHandle].alterGameState) { + cls.state = CA_DISCONNECTED; + // we can't just do a vstr nextmap, because + // if we are aborting the intro cinematic with + // a devmap command, nextmap would be valid by + // the time it was referenced + s = Cvar_VariableString( "nextmap" ); + if ( s[0] ) { + Cbuf_ExecuteText( EXEC_APPEND, va("%s\n", s) ); + Cvar_Set( "nextmap", "" ); + } + CL_handle = -1; + } + cinTable[currentHandle].fileName[0] = 0; + currentHandle = -1; +} + +/* +================== +SCR_StopCinematic +================== +*/ +e_status CIN_StopCinematic(int handle) { + + if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return FMV_EOF; + currentHandle = handle; + + Com_DPrintf("trFMV::stop(), closing %s\n", cinTable[currentHandle].fileName); + + if (!cinTable[currentHandle].buf) { + return FMV_EOF; + } + + if (cinTable[currentHandle].alterGameState) { + if ( cls.state != CA_CINEMATIC ) { + return cinTable[currentHandle].status; + } + } + cinTable[currentHandle].status = FMV_EOF; + RoQShutdown(); + + return FMV_EOF; +} + +/* +================== +SCR_RunCinematic + +Fetch and decompress the pending frame +================== +*/ + + +e_status CIN_RunCinematic (int handle) +{ + int start = 0; + int thisTime = 0; + + if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return FMV_EOF; + + if (cin.currentHandle != handle) { + currentHandle = handle; + cin.currentHandle = currentHandle; + cinTable[currentHandle].status = FMV_EOF; + RoQReset(); + } + + if (cinTable[handle].playonwalls < -1) + { + return cinTable[handle].status; + } + + currentHandle = handle; + + if (cinTable[currentHandle].alterGameState) { + if ( cls.state != CA_CINEMATIC ) { + return cinTable[currentHandle].status; + } + } + + if (cinTable[currentHandle].status == FMV_IDLE) { + return cinTable[currentHandle].status; + } + + // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer + thisTime = CL_ScaledMilliseconds()*com_timescale->value; + if (cinTable[currentHandle].shader && (abs(thisTime - cinTable[currentHandle].lastTime))>100) { + cinTable[currentHandle].startTime += thisTime - cinTable[currentHandle].lastTime; + } + // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer + cinTable[currentHandle].tfps = ((((CL_ScaledMilliseconds()*com_timescale->value) - cinTable[currentHandle].startTime)*3)/100); + + start = cinTable[currentHandle].startTime; + while( (cinTable[currentHandle].tfps != cinTable[currentHandle].numQuads) + && (cinTable[currentHandle].status == FMV_PLAY) ) + { + RoQInterrupt(); + if (start != cinTable[currentHandle].startTime) { + // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer + cinTable[currentHandle].tfps = ((((CL_ScaledMilliseconds()*com_timescale->value) + - cinTable[currentHandle].startTime)*3)/100); + start = cinTable[currentHandle].startTime; + } + } + + cinTable[currentHandle].lastTime = thisTime; + + if (cinTable[currentHandle].status == FMV_LOOPED) { + cinTable[currentHandle].status = FMV_PLAY; + } + + if (cinTable[currentHandle].status == FMV_EOF) { + if (cinTable[currentHandle].looping) { + RoQReset(); + } else { + RoQShutdown(); + } + } + + return cinTable[currentHandle].status; +} + +/* +================== +CL_PlayCinematic + +================== +*/ +int CIN_PlayCinematic( const char *arg, int x, int y, int w, int h, int systemBits ) { + unsigned short RoQID; + char name[MAX_OSPATH]; + int i; + + if (strstr(arg, "/") == NULL && strstr(arg, "\\") == NULL) { + Com_sprintf (name, sizeof(name), "video/%s", arg); + } else { + Com_sprintf (name, sizeof(name), "%s", arg); + } + + if (!(systemBits & CIN_system)) { + for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) { + if (!strcmp(cinTable[i].fileName, name) ) { + return i; + } + } + } + + Com_DPrintf("SCR_PlayCinematic( %s )\n", arg); + + Com_Memset(&cin, 0, sizeof(cinematics_t) ); + currentHandle = CIN_HandleForVideo(); + + cin.currentHandle = currentHandle; + + strcpy(cinTable[currentHandle].fileName, name); + + cinTable[currentHandle].ROQSize = 0; + cinTable[currentHandle].ROQSize = FS_FOpenFileRead (cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, qtrue); + + if (cinTable[currentHandle].ROQSize<=0) { + Com_DPrintf("play(%s), ROQSize<=0\n", arg); + cinTable[currentHandle].fileName[0] = 0; + return -1; + } + + CIN_SetExtents(currentHandle, x, y, w, h); + CIN_SetLooping(currentHandle, (systemBits & CIN_loop)!=0); + + cinTable[currentHandle].CIN_HEIGHT = DEFAULT_CIN_HEIGHT; + cinTable[currentHandle].CIN_WIDTH = DEFAULT_CIN_WIDTH; + cinTable[currentHandle].holdAtEnd = (systemBits & CIN_hold) != 0; + cinTable[currentHandle].alterGameState = (systemBits & CIN_system) != 0; + cinTable[currentHandle].playonwalls = 1; + cinTable[currentHandle].silent = (systemBits & CIN_silent) != 0; + cinTable[currentHandle].shader = (systemBits & CIN_shader) != 0; + + if (cinTable[currentHandle].alterGameState) { + // close the menu + if ( uivm ) { + VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE ); + } + } else { + cinTable[currentHandle].playonwalls = cl_inGameVideo->integer; + } + + initRoQ(); + + FS_Read (cin.file, 16, cinTable[currentHandle].iFile); + + RoQID = (unsigned short)(cin.file[0]) + (unsigned short)(cin.file[1])*256; + if (RoQID == 0x1084) + { + RoQ_init(); +// FS_Read (cin.file, cinTable[currentHandle].RoQFrameSize+8, cinTable[currentHandle].iFile); + + cinTable[currentHandle].status = FMV_PLAY; + Com_DPrintf("trFMV::play(), playing %s\n", arg); + + if (cinTable[currentHandle].alterGameState) { + cls.state = CA_CINEMATIC; + } + + Con_Close(); + + s_rawend[0] = s_soundtime; + + return currentHandle; + } + Com_DPrintf("trFMV::play(), invalid RoQ ID\n"); + + RoQShutdown(); + return -1; +} + +void CIN_SetExtents (int handle, int x, int y, int w, int h) { + if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return; + cinTable[handle].xpos = x; + cinTable[handle].ypos = y; + cinTable[handle].width = w; + cinTable[handle].height = h; + cinTable[handle].dirty = qtrue; +} + +void CIN_SetLooping(int handle, qboolean loop) { + if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return; + cinTable[handle].looping = loop; +} + +/* +================== +SCR_DrawCinematic + +================== +*/ +void CIN_DrawCinematic (int handle) { + float x, y, w, h; + byte *buf; + + if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return; + + if (!cinTable[handle].buf) { + return; + } + + x = cinTable[handle].xpos; + y = cinTable[handle].ypos; + w = cinTable[handle].width; + h = cinTable[handle].height; + buf = cinTable[handle].buf; + SCR_AdjustFrom640( &x, &y, &w, &h ); + + if (cinTable[handle].dirty && (cinTable[handle].CIN_WIDTH != cinTable[handle].drawX || cinTable[handle].CIN_HEIGHT != cinTable[handle].drawY)) { + int ix, iy, *buf2, *buf3, xm, ym, ll; + + xm = cinTable[handle].CIN_WIDTH/256; + ym = cinTable[handle].CIN_HEIGHT/256; + ll = 8; + if (cinTable[handle].CIN_WIDTH==512) { + ll = 9; + } + + buf3 = (int*)buf; + buf2 = Hunk_AllocateTempMemory( 256*256*4 ); + if (xm==2 && ym==2) { + byte *bc2, *bc3; + int ic, iiy; + + bc2 = (byte *)buf2; + bc3 = (byte *)buf3; + for (iy = 0; iy<256; iy++) { + iiy = iy<<12; + for (ix = 0; ix<2048; ix+=8) { + for(ic = ix;ic<(ix+4);ic++) { + *bc2=(bc3[iiy+ic]+bc3[iiy+4+ic]+bc3[iiy+2048+ic]+bc3[iiy+2048+4+ic])>>2; + bc2++; + } + } + } + } else if (xm==2 && ym==1) { + byte *bc2, *bc3; + int ic, iiy; + + bc2 = (byte *)buf2; + bc3 = (byte *)buf3; + for (iy = 0; iy<256; iy++) { + iiy = iy<<11; + for (ix = 0; ix<2048; ix+=8) { + for(ic = ix;ic<(ix+4);ic++) { + *bc2=(bc3[iiy+ic]+bc3[iiy+4+ic])>>1; + bc2++; + } + } + } + } else { + for (iy = 0; iy<256; iy++) { + for (ix = 0; ix<256; ix++) { + buf2[(iy<<8)+ix] = buf3[((iy*ym)<= 0) { + do { + SCR_RunCinematic(); + } while (cinTable[currentHandle].buf == NULL && cinTable[currentHandle].status == FMV_PLAY); // wait for first frame (load codebook and sound) + } +} + + +void SCR_DrawCinematic (void) { + if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) { + CIN_DrawCinematic(CL_handle); + } +} + +void SCR_RunCinematic (void) +{ + if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) { + CIN_RunCinematic(CL_handle); + } +} + +void SCR_StopCinematic(void) { + if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) { + CIN_StopCinematic(CL_handle); + S_StopAllSounds (); + CL_handle = -1; + } +} + +void CIN_UploadCinematic(int handle) { + if (handle >= 0 && handle < MAX_VIDEO_HANDLES) { + if (!cinTable[handle].buf) { + return; + } + if (cinTable[handle].playonwalls <= 0 && cinTable[handle].dirty) { + if (cinTable[handle].playonwalls == 0) { + cinTable[handle].playonwalls = -1; + } else { + if (cinTable[handle].playonwalls == -1) { + cinTable[handle].playonwalls = -2; + } else { + cinTable[handle].dirty = qfalse; + } + } + } + re.UploadCinematic( 256, 256, 256, 256, cinTable[handle].buf, handle, cinTable[handle].dirty); + if (cl_inGameVideo->integer == 0 && cinTable[handle].playonwalls == 1) { + cinTable[handle].playonwalls--; + } + } +} + diff --git a/reaction/engine/code/client/cl_console.c b/reaction/engine/code/client/cl_console.c new file mode 100644 index 00000000..da575a31 --- /dev/null +++ b/reaction/engine/code/client/cl_console.c @@ -0,0 +1,795 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// console.c + +#include "client.h" + + +int g_console_field_width = 78; + + +#define NUM_CON_TIMES 4 + +#define CON_TEXTSIZE 32768 +typedef struct { + qboolean initialized; + + short text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + + int linewidth; // characters across screen + int totallines; // total lines in console scrollback + + float xadjust; // for wide aspect screens + + float displayFrac; // aproaches finalFrac at scr_conspeed + float finalFrac; // 0.0 to 1.0 lines of console to display + + int vislines; // in scanlines + + int times[NUM_CON_TIMES]; // cls.realtime time the line was generated + // for transparent notify lines + vec4_t color; +} console_t; + +extern console_t con; + +console_t con; + +cvar_t *con_conspeed; +cvar_t *con_notifytime; + +#define DEFAULT_CONSOLE_WIDTH 78 + +vec4_t console_color = {1.0, 1.0, 1.0, 1.0}; + + +/* +================ +Con_ToggleConsole_f +================ +*/ +void Con_ToggleConsole_f (void) { + // Can't toggle the console when it's the only thing available + if ( cls.state == CA_DISCONNECTED && Key_GetCatcher( ) == KEYCATCH_CONSOLE ) { + return; + } + + Field_Clear( &g_consoleField ); + g_consoleField.widthInChars = g_console_field_width; + + Con_ClearNotify (); + Key_SetCatcher( Key_GetCatcher( ) ^ KEYCATCH_CONSOLE ); +} + +/* +================ +Con_MessageMode_f +================ +*/ +void Con_MessageMode_f (void) { + chat_playerNum = -1; + chat_team = qfalse; + Field_Clear( &chatField ); + chatField.widthInChars = 30; + + Key_SetCatcher( Key_GetCatcher( ) ^ KEYCATCH_MESSAGE ); +} + +/* +================ +Con_MessageMode2_f +================ +*/ +void Con_MessageMode2_f (void) { + chat_playerNum = -1; + chat_team = qtrue; + Field_Clear( &chatField ); + chatField.widthInChars = 25; + Key_SetCatcher( Key_GetCatcher( ) ^ KEYCATCH_MESSAGE ); +} + +/* +================ +Con_MessageMode3_f +================ +*/ +void Con_MessageMode3_f (void) { + chat_playerNum = VM_Call( cgvm, CG_CROSSHAIR_PLAYER ); + if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) { + chat_playerNum = -1; + return; + } + chat_team = qfalse; + Field_Clear( &chatField ); + chatField.widthInChars = 30; + Key_SetCatcher( Key_GetCatcher( ) ^ KEYCATCH_MESSAGE ); +} + +/* +================ +Con_MessageMode4_f +================ +*/ +void Con_MessageMode4_f (void) { + chat_playerNum = VM_Call( cgvm, CG_LAST_ATTACKER ); + if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) { + chat_playerNum = -1; + return; + } + chat_team = qfalse; + Field_Clear( &chatField ); + chatField.widthInChars = 30; + Key_SetCatcher( Key_GetCatcher( ) ^ KEYCATCH_MESSAGE ); +} + +/* +================ +Con_Clear_f +================ +*/ +void Con_Clear_f (void) { + int i; + + for ( i = 0 ; i < CON_TEXTSIZE ; i++ ) { + con.text[i] = (ColorIndex(COLOR_WHITE)<<8) | ' '; + } + + Con_Bottom(); // go to end +} + + +/* +================ +Con_Dump_f + +Save the console contents out to a file +================ +*/ +void Con_Dump_f (void) +{ + int l, x, i; + short *line; + fileHandle_t f; + char buffer[1024]; + + if (Cmd_Argc() != 2) + { + Com_Printf ("usage: condump \n"); + return; + } + + Com_Printf ("Dumped console text to %s.\n", Cmd_Argv(1) ); + + f = FS_FOpenFileWrite( Cmd_Argv( 1 ) ); + if (!f) + { + Com_Printf ("ERROR: couldn't open.\n"); + return; + } + + // skip empty lines + for (l = con.current - con.totallines + 1 ; l <= con.current ; l++) + { + line = con.text + (l%con.totallines)*con.linewidth; + for (x=0 ; x=0 ; x--) + { + if (buffer[x] == ' ') + buffer[x] = 0; + else + break; + } + strcat( buffer, "\n" ); + FS_Write(buffer, strlen(buffer), f); + } + + FS_FCloseFile( f ); +} + + +/* +================ +Con_ClearNotify +================ +*/ +void Con_ClearNotify( void ) { + int i; + + for ( i = 0 ; i < NUM_CON_TIMES ; i++ ) { + con.times[i] = 0; + } +} + + + +/* +================ +Con_CheckResize + +If the line width has changed, reformat the buffer. +================ +*/ +void Con_CheckResize (void) +{ + int i, j, width, oldwidth, oldtotallines, numlines, numchars; + short tbuf[CON_TEXTSIZE]; + + width = (SCREEN_WIDTH / SMALLCHAR_WIDTH) - 2; + + if (width == con.linewidth) + return; + + if (width < 1) // video hasn't been initialized yet + { + width = DEFAULT_CONSOLE_WIDTH; + con.linewidth = width; + con.totallines = CON_TEXTSIZE / con.linewidth; + for(i=0; i= 0) + { + if (skipnotify) + con.times[con.current % NUM_CON_TIMES] = 0; + else + con.times[con.current % NUM_CON_TIMES] = cls.realtime; + } + + con.x = 0; + if (con.display == con.current) + con.display++; + con.current++; + for(i=0; iinteger ) { + return; + } + + if (!con.initialized) { + con.color[0] = + con.color[1] = + con.color[2] = + con.color[3] = 1.0f; + con.linewidth = -1; + Con_CheckResize (); + con.initialized = qtrue; + } + + color = ColorIndex(COLOR_WHITE); + + while ( (c = *txt) != 0 ) { + if ( Q_IsColorString( txt ) ) { + color = ColorIndex( *(txt+1) ); + txt += 2; + continue; + } + + // count word length + for (l=0 ; l< con.linewidth ; l++) { + if ( txt[l] <= ' ') { + break; + } + + } + + // word wrap + if (l != con.linewidth && (con.x + l >= con.linewidth) ) { + Con_Linefeed(skipnotify); + + } + + txt++; + + switch (c) + { + case '\n': + Con_Linefeed (skipnotify); + break; + case '\r': + con.x = 0; + break; + default: // display character and advance + y = con.current % con.totallines; + con.text[y*con.linewidth+con.x] = (color << 8) | c; + con.x++; + if (con.x >= con.linewidth) { + Con_Linefeed(skipnotify); + con.x = 0; + } + break; + } + } + + + // mark time for transparent overlay + if (con.current >= 0) { + // NERVE - SMF + if ( skipnotify ) { + prev = con.current % NUM_CON_TIMES - 1; + if ( prev < 0 ) + prev = NUM_CON_TIMES - 1; + con.times[prev] = 0; + } + else + // -NERVE - SMF + con.times[con.current % NUM_CON_TIMES] = cls.realtime; + } +} + + +/* +============================================================================== + +DRAWING + +============================================================================== +*/ + + +/* +================ +Con_DrawInput + +Draw the editline after a ] prompt +================ +*/ +void Con_DrawInput (void) { + int y; + + if ( cls.state != CA_DISCONNECTED && !(Key_GetCatcher( ) & KEYCATCH_CONSOLE ) ) { + return; + } + + y = con.vislines - ( SMALLCHAR_HEIGHT * 2 ); + + re.SetColor( con.color ); + + SCR_DrawSmallChar( con.xadjust + 1 * SMALLCHAR_WIDTH, y, ']' ); + + Field_Draw( &g_consoleField, con.xadjust + 2 * SMALLCHAR_WIDTH, y, + SCREEN_WIDTH - 3 * SMALLCHAR_WIDTH, qtrue, qtrue ); +} + + +/* +================ +Con_DrawNotify + +Draws the last few lines of output transparently over the game top +================ +*/ +void Con_DrawNotify (void) +{ + int x, v; + short *text; + int i; + int time; + int skip; + int currentColor; + + currentColor = 7; + re.SetColor( g_color_table[currentColor] ); + + v = 0; + for (i= con.current-NUM_CON_TIMES+1 ; i<=con.current ; i++) + { + if (i < 0) + continue; + time = con.times[i % NUM_CON_TIMES]; + if (time == 0) + continue; + time = cls.realtime - time; + if (time > con_notifytime->value*1000) + continue; + text = con.text + (i % con.totallines)*con.linewidth; + + if (cl.snap.ps.pm_type != PM_INTERMISSION && Key_GetCatcher( ) & (KEYCATCH_UI | KEYCATCH_CGAME) ) { + continue; + } + + for (x = 0 ; x < con.linewidth ; x++) { + if ( ( text[x] & 0xff ) == ' ' ) { + continue; + } + if ( ( (text[x]>>8)&7 ) != currentColor ) { + currentColor = (text[x]>>8)&7; + re.SetColor( g_color_table[currentColor] ); + } + SCR_DrawSmallChar( cl_conXOffset->integer + con.xadjust + (x+1)*SMALLCHAR_WIDTH, v, text[x] & 0xff ); + } + + v += SMALLCHAR_HEIGHT; + } + + re.SetColor( NULL ); + + if (Key_GetCatcher( ) & (KEYCATCH_UI | KEYCATCH_CGAME) ) { + return; + } + + // draw the chat line + if ( Key_GetCatcher( ) & KEYCATCH_MESSAGE ) + { + if (chat_team) + { + SCR_DrawBigString (8, v, "say_team:", 1.0f, qfalse ); + skip = 10; + } + else + { + SCR_DrawBigString (8, v, "say:", 1.0f, qfalse ); + skip = 5; + } + + Field_BigDraw( &chatField, skip * BIGCHAR_WIDTH, v, + SCREEN_WIDTH - ( skip + 1 ) * BIGCHAR_WIDTH, qtrue, qtrue ); + + v += BIGCHAR_HEIGHT; + } + +} + +/* +================ +Con_DrawSolidConsole + +Draws the console with the solid background +================ +*/ +void Con_DrawSolidConsole( float frac ) { + int i, x, y; + int rows; + short *text; + int row; + int lines; +// qhandle_t conShader; + int currentColor; + vec4_t color; + + lines = cls.glconfig.vidHeight * frac; + if (lines <= 0) + return; + + if (lines > cls.glconfig.vidHeight ) + lines = cls.glconfig.vidHeight; + + // on wide screens, we will center the text + con.xadjust = 0; + SCR_AdjustFrom640( &con.xadjust, NULL, NULL, NULL ); + + // draw the background + y = frac * SCREEN_HEIGHT; + if ( y < 1 ) { + y = 0; + } + else { + SCR_DrawPic( 0, 0, SCREEN_WIDTH, y, cls.consoleShader ); + } + + color[0] = 1; + color[1] = 0; + color[2] = 0; + color[3] = 1; + SCR_FillRect( 0, y, SCREEN_WIDTH, 2, color ); + + + // draw the version number + + re.SetColor( g_color_table[ColorIndex(COLOR_RED)] ); + + i = strlen( Q3_VERSION ); + + for (x=0 ; x= con.totallines) { + // past scrollback wrap point + continue; + } + + text = con.text + (row % con.totallines)*con.linewidth; + + for (x=0 ; x>8)&7 ) != currentColor ) { + currentColor = (text[x]>>8)&7; + re.SetColor( g_color_table[currentColor] ); + } + SCR_DrawSmallChar( con.xadjust + (x+1)*SMALLCHAR_WIDTH, y, text[x] & 0xff ); + } + } + + // draw the input prompt, user text, and cursor if desired + Con_DrawInput (); + + re.SetColor( NULL ); +} + + + +/* +================== +Con_DrawConsole +================== +*/ +void Con_DrawConsole( void ) { + // check for console width changes from a vid mode change + Con_CheckResize (); + + // if disconnected, render console full screen + if ( cls.state == CA_DISCONNECTED ) { + if ( !( Key_GetCatcher( ) & (KEYCATCH_UI | KEYCATCH_CGAME)) ) { + Con_DrawSolidConsole( 1.0 ); + return; + } + } + + if ( con.displayFrac ) { + Con_DrawSolidConsole( con.displayFrac ); + } else { + // draw notify lines + if ( cls.state == CA_ACTIVE ) { + Con_DrawNotify (); + } + } +} + +//================================================================ + +/* +================== +Con_RunConsole + +Scroll it up or down +================== +*/ +void Con_RunConsole (void) { + // decide on the destination height of the console + if ( Key_GetCatcher( ) & KEYCATCH_CONSOLE ) + con.finalFrac = 0.5; // half screen + else + con.finalFrac = 0; // none visible + + // scroll towards the destination height + if (con.finalFrac < con.displayFrac) + { + con.displayFrac -= con_conspeed->value*cls.realFrametime*0.001; + if (con.finalFrac > con.displayFrac) + con.displayFrac = con.finalFrac; + + } + else if (con.finalFrac > con.displayFrac) + { + con.displayFrac += con_conspeed->value*cls.realFrametime*0.001; + if (con.finalFrac < con.displayFrac) + con.displayFrac = con.finalFrac; + } + +} + + +void Con_PageUp( void ) { + con.display -= 2; + if ( con.current - con.display >= con.totallines ) { + con.display = con.current - con.totallines + 1; + } +} + +void Con_PageDown( void ) { + con.display += 2; + if (con.display > con.current) { + con.display = con.current; + } +} + +void Con_Top( void ) { + con.display = con.totallines; + if ( con.current - con.display >= con.totallines ) { + con.display = con.current - con.totallines + 1; + } +} + +void Con_Bottom( void ) { + con.display = con.current; +} + + +void Con_Close( void ) { + if ( !com_cl_running->integer ) { + return; + } + Field_Clear( &g_consoleField ); + Con_ClearNotify (); + Key_SetCatcher( Key_GetCatcher( ) & ~KEYCATCH_CONSOLE ); + con.finalFrac = 0; // none visible + con.displayFrac = 0; +} diff --git a/reaction/engine/code/client/cl_curl.c b/reaction/engine/code/client/cl_curl.c new file mode 100644 index 00000000..bb6d7684 --- /dev/null +++ b/reaction/engine/code/client/cl_curl.c @@ -0,0 +1,339 @@ +/* +=========================================================================== +Copyright (C) 2006 Tony J. White (tjw@tjw.org) + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#ifdef USE_CURL +#include "client.h" +cvar_t *cl_cURLLib; + +#ifdef USE_CURL_DLOPEN +#include "../sys/sys_loadlib.h" + +char* (*qcurl_version)(void); + +CURL* (*qcurl_easy_init)(void); +CURLcode (*qcurl_easy_setopt)(CURL *curl, CURLoption option, ...); +CURLcode (*qcurl_easy_perform)(CURL *curl); +void (*qcurl_easy_cleanup)(CURL *curl); +CURLcode (*qcurl_easy_getinfo)(CURL *curl, CURLINFO info, ...); +CURL* (*qcurl_easy_duphandle)(CURL *curl); +void (*qcurl_easy_reset)(CURL *curl); +const char *(*qcurl_easy_strerror)(CURLcode); + +CURLM* (*qcurl_multi_init)(void); +CURLMcode (*qcurl_multi_add_handle)(CURLM *multi_handle, + CURL *curl_handle); +CURLMcode (*qcurl_multi_remove_handle)(CURLM *multi_handle, + CURL *curl_handle); +CURLMcode (*qcurl_multi_fdset)(CURLM *multi_handle, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); +CURLMcode (*qcurl_multi_perform)(CURLM *multi_handle, + int *running_handles); +CURLMcode (*qcurl_multi_cleanup)(CURLM *multi_handle); +CURLMsg *(*qcurl_multi_info_read)(CURLM *multi_handle, + int *msgs_in_queue); +const char *(*qcurl_multi_strerror)(CURLMcode); + +static void *cURLLib = NULL; + +/* +================= +GPA +================= +*/ +static void *GPA(char *str) +{ + void *rv; + + rv = Sys_LoadFunction(cURLLib, str); + if(!rv) + { + Com_Printf("Can't load symbol %s\n", str); + clc.cURLEnabled = qfalse; + return NULL; + } + else + { + Com_DPrintf("Loaded symbol %s (0x%p)\n", str, rv); + return rv; + } +} +#endif /* USE_CURL_DLOPEN */ + +/* +================= +CL_cURL_Init +================= +*/ +qboolean CL_cURL_Init() +{ +#ifdef USE_CURL_DLOPEN + if(cURLLib) + return qtrue; + + + Com_Printf("Loading \"%s\"...", cl_cURLLib->string); + if( (cURLLib = Sys_LoadLibrary(cl_cURLLib->string)) == 0 ) + { +#ifdef _WIN32 + return qfalse; +#else + char fn[1024]; + + Q_strncpyz( fn, Sys_Cwd( ), sizeof( fn ) ); + strncat(fn, "/", sizeof(fn)-strlen(fn)-1); + strncat(fn, cl_cURLLib->string, sizeof(fn)-strlen(fn)-1); + + if((cURLLib = Sys_LoadLibrary(fn)) == 0) + { +#ifdef ALTERNATE_CURL_LIB + // On some linux distributions there is no libcurl.so.3, but only libcurl.so.4. That one works too. + if( (cURLLib = Sys_LoadLibrary(ALTERNATE_CURL_LIB)) == 0 ) + { + return qfalse; + } +#else + return qfalse; +#endif + } +#endif /* _WIN32 */ + } + + clc.cURLEnabled = qtrue; + + qcurl_version = GPA("curl_version"); + + qcurl_easy_init = GPA("curl_easy_init"); + qcurl_easy_setopt = GPA("curl_easy_setopt"); + qcurl_easy_perform = GPA("curl_easy_perform"); + qcurl_easy_cleanup = GPA("curl_easy_cleanup"); + qcurl_easy_getinfo = GPA("curl_easy_getinfo"); + qcurl_easy_duphandle = GPA("curl_easy_duphandle"); + qcurl_easy_reset = GPA("curl_easy_reset"); + qcurl_easy_strerror = GPA("curl_easy_strerror"); + + qcurl_multi_init = GPA("curl_multi_init"); + qcurl_multi_add_handle = GPA("curl_multi_add_handle"); + qcurl_multi_remove_handle = GPA("curl_multi_remove_handle"); + qcurl_multi_fdset = GPA("curl_multi_fdset"); + qcurl_multi_perform = GPA("curl_multi_perform"); + qcurl_multi_cleanup = GPA("curl_multi_cleanup"); + qcurl_multi_info_read = GPA("curl_multi_info_read"); + qcurl_multi_strerror = GPA("curl_multi_strerror"); + + if(!clc.cURLEnabled) + { + CL_cURL_Shutdown(); + Com_Printf("FAIL One or more symbols not found\n"); + return qfalse; + } + Com_Printf("OK\n"); + + return qtrue; +#else + clc.cURLEnabled = qtrue; + return qtrue; +#endif /* USE_CURL_DLOPEN */ +} + +/* +================= +CL_cURL_Shutdown +================= +*/ +void CL_cURL_Shutdown( void ) +{ + CL_cURL_Cleanup(); +#ifdef USE_CURL_DLOPEN + if(cURLLib) + { + Sys_UnloadLibrary(cURLLib); + cURLLib = NULL; + } + qcurl_easy_init = NULL; + qcurl_easy_setopt = NULL; + qcurl_easy_perform = NULL; + qcurl_easy_cleanup = NULL; + qcurl_easy_getinfo = NULL; + qcurl_easy_duphandle = NULL; + qcurl_easy_reset = NULL; + + qcurl_multi_init = NULL; + qcurl_multi_add_handle = NULL; + qcurl_multi_remove_handle = NULL; + qcurl_multi_fdset = NULL; + qcurl_multi_perform = NULL; + qcurl_multi_cleanup = NULL; + qcurl_multi_info_read = NULL; + qcurl_multi_strerror = NULL; +#endif /* USE_CURL_DLOPEN */ +} + +void CL_cURL_Cleanup(void) +{ + if(clc.downloadCURLM) { + if(clc.downloadCURL) { + qcurl_multi_remove_handle(clc.downloadCURLM, + clc.downloadCURL); + qcurl_easy_cleanup(clc.downloadCURL); + } + qcurl_multi_cleanup(clc.downloadCURLM); + clc.downloadCURLM = NULL; + clc.downloadCURL = NULL; + } + else if(clc.downloadCURL) { + qcurl_easy_cleanup(clc.downloadCURL); + clc.downloadCURL = NULL; + } +} + +static int CL_cURL_CallbackProgress( void *dummy, double dltotal, double dlnow, + double ultotal, double ulnow ) +{ + clc.downloadSize = (int)dltotal; + Cvar_SetValue( "cl_downloadSize", clc.downloadSize ); + clc.downloadCount = (int)dlnow; + Cvar_SetValue( "cl_downloadCount", clc.downloadCount ); + return 0; +} + +static size_t CL_cURL_CallbackWrite(void *buffer, size_t size, size_t nmemb, + void *stream) +{ + FS_Write( buffer, size*nmemb, ((fileHandle_t*)stream)[0] ); + return size*nmemb; +} + +void CL_cURL_BeginDownload( const char *localName, const char *remoteURL ) +{ + clc.cURLUsed = qtrue; + Com_Printf("URL: %s\n", remoteURL); + Com_DPrintf("***** CL_cURL_BeginDownload *****\n" + "Localname: %s\n" + "RemoteURL: %s\n" + "****************************\n", localName, remoteURL); + CL_cURL_Cleanup(); + Q_strncpyz(clc.downloadURL, remoteURL, sizeof(clc.downloadURL)); + Q_strncpyz(clc.downloadName, localName, sizeof(clc.downloadName)); + Com_sprintf(clc.downloadTempName, sizeof(clc.downloadTempName), + "%s.tmp", localName); + + // Set so UI gets access to it + Cvar_Set("cl_downloadName", localName); + Cvar_Set("cl_downloadSize", "0"); + Cvar_Set("cl_downloadCount", "0"); + Cvar_SetValue("cl_downloadTime", cls.realtime); + + clc.downloadBlock = 0; // Starting new file + clc.downloadCount = 0; + + clc.downloadCURL = qcurl_easy_init(); + if(!clc.downloadCURL) { + Com_Error(ERR_DROP, "CL_cURL_BeginDownload: qcurl_easy_init() " + "failed\n"); + return; + } + clc.download = FS_SV_FOpenFileWrite(clc.downloadTempName); + if(!clc.download) { + Com_Error(ERR_DROP, "CL_cURL_BeginDownload: failed to open " + "%s for writing\n", clc.downloadTempName); + return; + } + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEDATA, clc.download); + if(com_developer->integer) + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_VERBOSE, 1); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_URL, clc.downloadURL); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_TRANSFERTEXT, 0); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_REFERER, va("ioQ3://%s", + NET_AdrToString(clc.serverAddress))); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_USERAGENT, va("%s %s", + Q3_VERSION, qcurl_version())); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEFUNCTION, + CL_cURL_CallbackWrite); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEDATA, &clc.download); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_NOPROGRESS, 0); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_PROGRESSFUNCTION, + CL_cURL_CallbackProgress); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_PROGRESSDATA, NULL); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_FAILONERROR, 1); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_FOLLOWLOCATION, 1); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_MAXREDIRS, 5); + clc.downloadCURLM = qcurl_multi_init(); + if(!clc.downloadCURLM) { + qcurl_easy_cleanup(clc.downloadCURL); + clc.downloadCURL = NULL; + Com_Error(ERR_DROP, "CL_cURL_BeginDownload: qcurl_multi_init() " + "failed\n"); + return; + } + qcurl_multi_add_handle(clc.downloadCURLM, clc.downloadCURL); + + if(!(clc.sv_allowDownload & DLF_NO_DISCONNECT) && + !clc.cURLDisconnected) { + + CL_AddReliableCommand("disconnect"); + CL_WritePacket(); + CL_WritePacket(); + CL_WritePacket(); + clc.cURLDisconnected = qtrue; + } +} + +void CL_cURL_PerformDownload(void) +{ + CURLMcode res; + CURLMsg *msg; + int c; + int i = 0; + + res = qcurl_multi_perform(clc.downloadCURLM, &c); + while(res == CURLM_CALL_MULTI_PERFORM && i < 100) { + res = qcurl_multi_perform(clc.downloadCURLM, &c); + i++; + } + if(res == CURLM_CALL_MULTI_PERFORM) + return; + msg = qcurl_multi_info_read(clc.downloadCURLM, &c); + if(msg == NULL) { + return; + } + FS_FCloseFile(clc.download); + if(msg->msg == CURLMSG_DONE && msg->data.result == CURLE_OK) { + FS_SV_Rename(clc.downloadTempName, clc.downloadName); + clc.downloadRestart = qtrue; + } + else { + long code; + + qcurl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, + &code); + Com_Error(ERR_DROP, "Download Error: %s Code: %ld URL: %s", + qcurl_easy_strerror(msg->data.result), + code, clc.downloadURL); + } + *clc.downloadTempName = *clc.downloadName = 0; + Cvar_Set( "cl_downloadName", "" ); + CL_NextDownload(); +} +#endif /* USE_CURL */ diff --git a/reaction/engine/code/client/cl_curl.h b/reaction/engine/code/client/cl_curl.h new file mode 100644 index 00000000..c8d3006a --- /dev/null +++ b/reaction/engine/code/client/cl_curl.h @@ -0,0 +1,102 @@ +/* +=========================================================================== +Copyright (C) 2006 Tony J. White (tjw@tjw.org) + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + + +#ifndef __QCURL_H__ +#define __QCURL_H__ + +extern cvar_t *cl_cURLLib; + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" + +#ifdef WIN32 +#define DEFAULT_CURL_LIB "libcurl-3.dll" +#elif defined(MACOS_X) +#define DEFAULT_CURL_LIB "libcurl.dylib" +#else +#define DEFAULT_CURL_LIB "libcurl.so.4" +#define ALTERNATE_CURL_LIB "libcurl.so.3" +#endif + +#ifdef USE_LOCAL_HEADERS + #include "../libcurl/curl/curl.h" +#else + #include +#endif + + +#ifdef USE_CURL_DLOPEN +extern char* (*qcurl_version)(void); + +extern CURL* (*qcurl_easy_init)(void); +extern CURLcode (*qcurl_easy_setopt)(CURL *curl, CURLoption option, ...); +extern CURLcode (*qcurl_easy_perform)(CURL *curl); +extern void (*qcurl_easy_cleanup)(CURL *curl); +extern CURLcode (*qcurl_easy_getinfo)(CURL *curl, CURLINFO info, ...); +extern void (*qcurl_easy_reset)(CURL *curl); +extern const char *(*qcurl_easy_strerror)(CURLcode); + +extern CURLM* (*qcurl_multi_init)(void); +extern CURLMcode (*qcurl_multi_add_handle)(CURLM *multi_handle, + CURL *curl_handle); +extern CURLMcode (*qcurl_multi_remove_handle)(CURLM *multi_handle, + CURL *curl_handle); +extern CURLMcode (*qcurl_multi_fdset)(CURLM *multi_handle, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); +extern CURLMcode (*qcurl_multi_perform)(CURLM *multi_handle, + int *running_handles); +extern CURLMcode (*qcurl_multi_cleanup)(CURLM *multi_handle); +extern CURLMsg *(*qcurl_multi_info_read)(CURLM *multi_handle, + int *msgs_in_queue); +extern const char *(*qcurl_multi_strerror)(CURLMcode); +#else +#define qcurl_version curl_version + +#define qcurl_easy_init curl_easy_init +#define qcurl_easy_setopt curl_easy_setopt +#define qcurl_easy_perform curl_easy_perform +#define qcurl_easy_cleanup curl_easy_cleanup +#define qcurl_easy_getinfo curl_easy_getinfo +#define qcurl_easy_duphandle curl_easy_duphandle +#define qcurl_easy_reset curl_easy_reset +#define qcurl_easy_strerror curl_easy_strerror + +#define qcurl_multi_init curl_multi_init +#define qcurl_multi_add_handle curl_multi_add_handle +#define qcurl_multi_remove_handle curl_multi_remove_handle +#define qcurl_multi_fdset curl_multi_fdset +#define qcurl_multi_perform curl_multi_perform +#define qcurl_multi_cleanup curl_multi_cleanup +#define qcurl_multi_info_read curl_multi_info_read +#define qcurl_multi_strerror curl_multi_strerror +#endif + +qboolean CL_cURL_Init( void ); +void CL_cURL_Shutdown( void ); +void CL_cURL_BeginDownload( const char *localName, const char *remoteURL ); +void CL_cURL_PerformDownload( void ); +void CL_cURL_Cleanup( void ); +#endif // __QCURL_H__ diff --git a/reaction/engine/code/client/cl_input.c b/reaction/engine/code/client/cl_input.c new file mode 100644 index 00000000..72056a28 --- /dev/null +++ b/reaction/engine/code/client/cl_input.c @@ -0,0 +1,1009 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// cl.input.c -- builds an intended movement command to send to the server + +#include "client.h" + +unsigned frame_msec; +int old_com_frameTime; + +/* +=============================================================================== + +KEY BUTTONS + +Continuous button event tracking is complicated by the fact that two different +input sources (say, mouse button 1 and the control key) can both press the +same button, but the button should only be released when both of the +pressing key have been released. + +When a key event issues a button command (+forward, +attack, etc), it appends +its key number as argv(1) so it can be matched up with the release. + +argv(2) will be set to the time the event happened, which allows exact +control even at low framerates when the down and up events may both get qued +at the same time. + +=============================================================================== +*/ + + +kbutton_t in_left, in_right, in_forward, in_back; +kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; +kbutton_t in_strafe, in_speed; +kbutton_t in_up, in_down; + +#ifdef USE_VOIP +kbutton_t in_voiprecord; +#endif + +kbutton_t in_buttons[16]; + + +qboolean in_mlooking; + + +void IN_MLookDown( void ) { + in_mlooking = qtrue; +} + +void IN_MLookUp( void ) { + in_mlooking = qfalse; + if ( !cl_freelook->integer ) { + IN_CenterView (); + } +} + +void IN_KeyDown( kbutton_t *b ) { + int k; + char *c; + + c = Cmd_Argv(1); + if ( c[0] ) { + k = atoi(c); + } else { + k = -1; // typed manually at the console for continuous down + } + + if ( k == b->down[0] || k == b->down[1] ) { + return; // repeating key + } + + if ( !b->down[0] ) { + b->down[0] = k; + } else if ( !b->down[1] ) { + b->down[1] = k; + } else { + Com_Printf ("Three keys down for a button!\n"); + return; + } + + if ( b->active ) { + return; // still down + } + + // save timestamp for partial frame summing + c = Cmd_Argv(2); + b->downtime = atoi(c); + + b->active = qtrue; + b->wasPressed = qtrue; +} + +void IN_KeyUp( kbutton_t *b ) { + int k; + char *c; + unsigned uptime; + + c = Cmd_Argv(1); + if ( c[0] ) { + k = atoi(c); + } else { + // typed manually at the console, assume for unsticking, so clear all + b->down[0] = b->down[1] = 0; + b->active = qfalse; + return; + } + + if ( b->down[0] == k ) { + b->down[0] = 0; + } else if ( b->down[1] == k ) { + b->down[1] = 0; + } else { + return; // key up without coresponding down (menu pass through) + } + if ( b->down[0] || b->down[1] ) { + return; // some other key is still holding it down + } + + b->active = qfalse; + + // save timestamp for partial frame summing + c = Cmd_Argv(2); + uptime = atoi(c); + if ( uptime ) { + b->msec += uptime - b->downtime; + } else { + b->msec += frame_msec / 2; + } + + b->active = qfalse; +} + + + +/* +=============== +CL_KeyState + +Returns the fraction of the frame that the key was down +=============== +*/ +float CL_KeyState( kbutton_t *key ) { + float val; + int msec; + + msec = key->msec; + key->msec = 0; + + if ( key->active ) { + // still down + if ( !key->downtime ) { + msec = com_frameTime; + } else { + msec += com_frameTime - key->downtime; + } + key->downtime = com_frameTime; + } + +#if 0 + if (msec) { + Com_Printf ("%i ", msec); + } +#endif + + val = (float)msec / frame_msec; + if ( val < 0 ) { + val = 0; + } + if ( val > 1 ) { + val = 1; + } + + return val; +} + + + +void IN_UpDown(void) {IN_KeyDown(&in_up);} +void IN_UpUp(void) {IN_KeyUp(&in_up);} +void IN_DownDown(void) {IN_KeyDown(&in_down);} +void IN_DownUp(void) {IN_KeyUp(&in_down);} +void IN_LeftDown(void) {IN_KeyDown(&in_left);} +void IN_LeftUp(void) {IN_KeyUp(&in_left);} +void IN_RightDown(void) {IN_KeyDown(&in_right);} +void IN_RightUp(void) {IN_KeyUp(&in_right);} +void IN_ForwardDown(void) {IN_KeyDown(&in_forward);} +void IN_ForwardUp(void) {IN_KeyUp(&in_forward);} +void IN_BackDown(void) {IN_KeyDown(&in_back);} +void IN_BackUp(void) {IN_KeyUp(&in_back);} +void IN_LookupDown(void) {IN_KeyDown(&in_lookup);} +void IN_LookupUp(void) {IN_KeyUp(&in_lookup);} +void IN_LookdownDown(void) {IN_KeyDown(&in_lookdown);} +void IN_LookdownUp(void) {IN_KeyUp(&in_lookdown);} +void IN_MoveleftDown(void) {IN_KeyDown(&in_moveleft);} +void IN_MoveleftUp(void) {IN_KeyUp(&in_moveleft);} +void IN_MoverightDown(void) {IN_KeyDown(&in_moveright);} +void IN_MoverightUp(void) {IN_KeyUp(&in_moveright);} + +void IN_SpeedDown(void) {IN_KeyDown(&in_speed);} +void IN_SpeedUp(void) {IN_KeyUp(&in_speed);} +void IN_StrafeDown(void) {IN_KeyDown(&in_strafe);} +void IN_StrafeUp(void) {IN_KeyUp(&in_strafe);} + +#ifdef USE_VOIP +void IN_VoipRecordDown(void) +{ + IN_KeyDown(&in_voiprecord); + Cvar_Set("cl_voipSend", "1"); +} + +void IN_VoipRecordUp(void) +{ + IN_KeyUp(&in_voiprecord); + Cvar_Set("cl_voipSend", "0"); +} +#endif + +void IN_Button0Down(void) {IN_KeyDown(&in_buttons[0]);} +void IN_Button0Up(void) {IN_KeyUp(&in_buttons[0]);} +void IN_Button1Down(void) {IN_KeyDown(&in_buttons[1]);} +void IN_Button1Up(void) {IN_KeyUp(&in_buttons[1]);} +void IN_Button2Down(void) {IN_KeyDown(&in_buttons[2]);} +void IN_Button2Up(void) {IN_KeyUp(&in_buttons[2]);} +void IN_Button3Down(void) {IN_KeyDown(&in_buttons[3]);} +void IN_Button3Up(void) {IN_KeyUp(&in_buttons[3]);} +void IN_Button4Down(void) {IN_KeyDown(&in_buttons[4]);} +void IN_Button4Up(void) {IN_KeyUp(&in_buttons[4]);} +void IN_Button5Down(void) {IN_KeyDown(&in_buttons[5]);} +void IN_Button5Up(void) {IN_KeyUp(&in_buttons[5]);} +void IN_Button6Down(void) {IN_KeyDown(&in_buttons[6]);} +void IN_Button6Up(void) {IN_KeyUp(&in_buttons[6]);} +void IN_Button7Down(void) {IN_KeyDown(&in_buttons[7]);} +void IN_Button7Up(void) {IN_KeyUp(&in_buttons[7]);} +void IN_Button8Down(void) {IN_KeyDown(&in_buttons[8]);} +void IN_Button8Up(void) {IN_KeyUp(&in_buttons[8]);} +void IN_Button9Down(void) {IN_KeyDown(&in_buttons[9]);} +void IN_Button9Up(void) {IN_KeyUp(&in_buttons[9]);} +void IN_Button10Down(void) {IN_KeyDown(&in_buttons[10]);} +void IN_Button10Up(void) {IN_KeyUp(&in_buttons[10]);} +void IN_Button11Down(void) {IN_KeyDown(&in_buttons[11]);} +void IN_Button11Up(void) {IN_KeyUp(&in_buttons[11]);} +void IN_Button12Down(void) {IN_KeyDown(&in_buttons[12]);} +void IN_Button12Up(void) {IN_KeyUp(&in_buttons[12]);} +void IN_Button13Down(void) {IN_KeyDown(&in_buttons[13]);} +void IN_Button13Up(void) {IN_KeyUp(&in_buttons[13]);} +void IN_Button14Down(void) {IN_KeyDown(&in_buttons[14]);} +void IN_Button14Up(void) {IN_KeyUp(&in_buttons[14]);} +void IN_Button15Down(void) {IN_KeyDown(&in_buttons[15]);} +void IN_Button15Up(void) {IN_KeyUp(&in_buttons[15]);} + +void IN_ButtonDown (void) { + IN_KeyDown(&in_buttons[1]);} +void IN_ButtonUp (void) { + IN_KeyUp(&in_buttons[1]);} + +void IN_CenterView (void) { + cl.viewangles[PITCH] = -SHORT2ANGLE(cl.snap.ps.delta_angles[PITCH]); +} + + +//========================================================================== + +cvar_t *cl_upspeed; +cvar_t *cl_forwardspeed; +cvar_t *cl_sidespeed; + +cvar_t *cl_yawspeed; +cvar_t *cl_pitchspeed; + +cvar_t *cl_run; + +cvar_t *cl_anglespeedkey; + + +/* +================ +CL_AdjustAngles + +Moves the local angle positions +================ +*/ +void CL_AdjustAngles( void ) { + float speed; + + if ( in_speed.active ) { + speed = 0.001 * cls.frametime * cl_anglespeedkey->value; + } else { + speed = 0.001 * cls.frametime; + } + + if ( !in_strafe.active ) { + cl.viewangles[YAW] -= speed*cl_yawspeed->value*CL_KeyState (&in_right); + cl.viewangles[YAW] += speed*cl_yawspeed->value*CL_KeyState (&in_left); + } + + cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * CL_KeyState (&in_lookup); + cl.viewangles[PITCH] += speed*cl_pitchspeed->value * CL_KeyState (&in_lookdown); +} + +/* +================ +CL_KeyMove + +Sets the usercmd_t based on key states +================ +*/ +void CL_KeyMove( usercmd_t *cmd ) { + int movespeed; + int forward, side, up; + + // + // adjust for speed key / running + // the walking flag is to keep animations consistant + // even during acceleration and develeration + // + if ( in_speed.active ^ cl_run->integer ) { + movespeed = 127; + cmd->buttons &= ~BUTTON_WALKING; + } else { + cmd->buttons |= BUTTON_WALKING; + movespeed = 64; + } + + forward = 0; + side = 0; + up = 0; + if ( in_strafe.active ) { + side += movespeed * CL_KeyState (&in_right); + side -= movespeed * CL_KeyState (&in_left); + } + + side += movespeed * CL_KeyState (&in_moveright); + side -= movespeed * CL_KeyState (&in_moveleft); + + + up += movespeed * CL_KeyState (&in_up); + up -= movespeed * CL_KeyState (&in_down); + + forward += movespeed * CL_KeyState (&in_forward); + forward -= movespeed * CL_KeyState (&in_back); + + cmd->forwardmove = ClampChar( forward ); + cmd->rightmove = ClampChar( side ); + cmd->upmove = ClampChar( up ); +} + +/* +================= +CL_MouseEvent +================= +*/ +void CL_MouseEvent( int dx, int dy, int time ) { + if ( Key_GetCatcher( ) & KEYCATCH_UI ) { + VM_Call( uivm, UI_MOUSE_EVENT, dx, dy ); + } else if (Key_GetCatcher( ) & KEYCATCH_CGAME) { + VM_Call (cgvm, CG_MOUSE_EVENT, dx, dy); + } else { + cl.mouseDx[cl.mouseIndex] += dx; + cl.mouseDy[cl.mouseIndex] += dy; + } +} + +/* +================= +CL_JoystickEvent + +Joystick values stay set until changed +================= +*/ +void CL_JoystickEvent( int axis, int value, int time ) { + if ( axis < 0 || axis >= MAX_JOYSTICK_AXIS ) { + Com_Error( ERR_DROP, "CL_JoystickEvent: bad axis %i", axis ); + } + cl.joystickAxis[axis] = value; +} + +/* +================= +CL_JoystickMove +================= +*/ +void CL_JoystickMove( usercmd_t *cmd ) { + int movespeed; + float anglespeed; + + if ( in_speed.active ^ cl_run->integer ) { + movespeed = 2; + } else { + movespeed = 1; + cmd->buttons |= BUTTON_WALKING; + } + + if ( in_speed.active ) { + anglespeed = 0.001 * cls.frametime * cl_anglespeedkey->value; + } else { + anglespeed = 0.001 * cls.frametime; + } + + if ( !in_strafe.active ) { + cl.viewangles[YAW] += anglespeed * cl_yawspeed->value * cl.joystickAxis[AXIS_SIDE]; + } else { + cmd->rightmove = ClampChar( cmd->rightmove + cl.joystickAxis[AXIS_SIDE] ); + } + + if ( in_mlooking ) { + cl.viewangles[PITCH] += anglespeed * cl_pitchspeed->value * cl.joystickAxis[AXIS_FORWARD]; + } else { + cmd->forwardmove = ClampChar( cmd->forwardmove + cl.joystickAxis[AXIS_FORWARD] ); + } + + cmd->upmove = ClampChar( cmd->upmove + cl.joystickAxis[AXIS_UP] ); +} + +/* +================= +CL_MouseMove +================= +*/ +void CL_MouseMove( usercmd_t *cmd ) { + float mx, my; + float accelSensitivity; + float rate; + + // allow mouse smoothing + if ( m_filter->integer ) { + mx = ( cl.mouseDx[0] + cl.mouseDx[1] ) * 0.5; + my = ( cl.mouseDy[0] + cl.mouseDy[1] ) * 0.5; + } else { + mx = cl.mouseDx[cl.mouseIndex]; + my = cl.mouseDy[cl.mouseIndex]; + } + cl.mouseIndex ^= 1; + cl.mouseDx[cl.mouseIndex] = 0; + cl.mouseDy[cl.mouseIndex] = 0; + + rate = sqrt( mx * mx + my * my ) / (float)frame_msec; + accelSensitivity = cl_sensitivity->value + rate * cl_mouseAccel->value; + + // scale by FOV + accelSensitivity *= cl.cgameSensitivity; + + if ( rate && cl_showMouseRate->integer ) { + Com_Printf( "%f : %f\n", rate, accelSensitivity ); + } + + mx *= accelSensitivity; + my *= accelSensitivity; + + if (!mx && !my) { + return; + } + + // add mouse X/Y movement to cmd + if ( in_strafe.active ) { + cmd->rightmove = ClampChar( cmd->rightmove + m_side->value * mx ); + } else { + cl.viewangles[YAW] -= m_yaw->value * mx; + } + + if ( (in_mlooking || cl_freelook->integer) && !in_strafe.active ) { + cl.viewangles[PITCH] += m_pitch->value * my; + } else { + cmd->forwardmove = ClampChar( cmd->forwardmove - m_forward->value * my ); + } +} + + +/* +============== +CL_CmdButtons +============== +*/ +void CL_CmdButtons( usercmd_t *cmd ) { + int i; + + // + // figure button bits + // send a button bit even if the key was pressed and released in + // less than a frame + // + for (i = 0 ; i < 15 ; i++) { + if ( in_buttons[i].active || in_buttons[i].wasPressed ) { + cmd->buttons |= 1 << i; + } + in_buttons[i].wasPressed = qfalse; + } + + if ( Key_GetCatcher( ) ) { + cmd->buttons |= BUTTON_TALK; + } + + // allow the game to know if any key at all is + // currently pressed, even if it isn't bound to anything + if ( anykeydown && Key_GetCatcher( ) == 0 ) { + cmd->buttons |= BUTTON_ANY; + } +} + + +/* +============== +CL_FinishMove +============== +*/ +void CL_FinishMove( usercmd_t *cmd ) { + int i; + + // copy the state that the cgame is currently sending + cmd->weapon = cl.cgameUserCmdValue; + + // send the current server time so the amount of movement + // can be determined without allowing cheating + cmd->serverTime = cl.serverTime; + + for (i=0 ; i<3 ; i++) { + cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]); + } +} + + +/* +================= +CL_CreateCmd +================= +*/ +usercmd_t CL_CreateCmd( void ) { + usercmd_t cmd; + vec3_t oldAngles; + + VectorCopy( cl.viewangles, oldAngles ); + + // keyboard angle adjustment + CL_AdjustAngles (); + + Com_Memset( &cmd, 0, sizeof( cmd ) ); + + CL_CmdButtons( &cmd ); + + // get basic movement from keyboard + CL_KeyMove( &cmd ); + + // get basic movement from mouse + CL_MouseMove( &cmd ); + + // get basic movement from joystick + CL_JoystickMove( &cmd ); + + // check to make sure the angles haven't wrapped + if ( cl.viewangles[PITCH] - oldAngles[PITCH] > 90 ) { + cl.viewangles[PITCH] = oldAngles[PITCH] + 90; + } else if ( oldAngles[PITCH] - cl.viewangles[PITCH] > 90 ) { + cl.viewangles[PITCH] = oldAngles[PITCH] - 90; + } + + // store out the final values + CL_FinishMove( &cmd ); + + // draw debug graphs of turning for mouse testing + if ( cl_debugMove->integer ) { + if ( cl_debugMove->integer == 1 ) { + SCR_DebugGraph( abs(cl.viewangles[YAW] - oldAngles[YAW]), 0 ); + } + if ( cl_debugMove->integer == 2 ) { + SCR_DebugGraph( abs(cl.viewangles[PITCH] - oldAngles[PITCH]), 0 ); + } + } + + return cmd; +} + + +/* +================= +CL_CreateNewCommands + +Create a new usercmd_t structure for this frame +================= +*/ +void CL_CreateNewCommands( void ) { + usercmd_t *cmd; + int cmdNum; + + // no need to create usercmds until we have a gamestate + if ( cls.state < CA_PRIMED ) { + return; + } + + frame_msec = com_frameTime - old_com_frameTime; + + // if running less than 5fps, truncate the extra time to prevent + // unexpected moves after a hitch + if ( frame_msec > 200 ) { + frame_msec = 200; + } + old_com_frameTime = com_frameTime; + + + // generate a command for this frame + cl.cmdNumber++; + cmdNum = cl.cmdNumber & CMD_MASK; + cl.cmds[cmdNum] = CL_CreateCmd (); + cmd = &cl.cmds[cmdNum]; +} + +/* +================= +CL_ReadyToSendPacket + +Returns qfalse if we are over the maxpackets limit +and should choke back the bandwidth a bit by not sending +a packet this frame. All the commands will still get +delivered in the next packet, but saving a header and +getting more delta compression will reduce total bandwidth. +================= +*/ +qboolean CL_ReadyToSendPacket( void ) { + int oldPacketNum; + int delta; + + // don't send anything if playing back a demo + if ( clc.demoplaying || cls.state == CA_CINEMATIC ) { + return qfalse; + } + + // If we are downloading, we send no less than 50ms between packets + if ( *clc.downloadTempName && + cls.realtime - clc.lastPacketSentTime < 50 ) { + return qfalse; + } + + // if we don't have a valid gamestate yet, only send + // one packet a second + if ( cls.state != CA_ACTIVE && + cls.state != CA_PRIMED && + !*clc.downloadTempName && + cls.realtime - clc.lastPacketSentTime < 1000 ) { + return qfalse; + } + + // send every frame for loopbacks + if ( clc.netchan.remoteAddress.type == NA_LOOPBACK ) { + return qtrue; + } + + // send every frame for LAN + if ( cl_lanForcePackets->integer && Sys_IsLANAddress( clc.netchan.remoteAddress ) ) { + return qtrue; + } + + // check for exceeding cl_maxpackets + if ( cl_maxpackets->integer < 15 ) { + Cvar_Set( "cl_maxpackets", "15" ); + } else if ( cl_maxpackets->integer > 125 ) { + Cvar_Set( "cl_maxpackets", "125" ); + } + oldPacketNum = (clc.netchan.outgoingSequence - 1) & PACKET_MASK; + delta = cls.realtime - cl.outPackets[ oldPacketNum ].p_realtime; + if ( delta < 1000 / cl_maxpackets->integer ) { + // the accumulated commands will go out in the next packet + return qfalse; + } + + return qtrue; +} + +/* +=================== +CL_WritePacket + +Create and send the command packet to the server +Including both the reliable commands and the usercmds + +During normal gameplay, a client packet will contain something like: + +4 sequence number +2 qport +4 serverid +4 acknowledged sequence number +4 clc.serverCommandSequence + +1 clc_move or clc_moveNoDelta +1 command count + + +=================== +*/ +void CL_WritePacket( void ) { + msg_t buf; + byte data[MAX_MSGLEN]; + int i, j; + usercmd_t *cmd, *oldcmd; + usercmd_t nullcmd; + int packetNum; + int oldPacketNum; + int count, key; + + // don't send anything if playing back a demo + if ( clc.demoplaying || cls.state == CA_CINEMATIC ) { + return; + } + + Com_Memset( &nullcmd, 0, sizeof(nullcmd) ); + oldcmd = &nullcmd; + + MSG_Init( &buf, data, sizeof(data) ); + + MSG_Bitstream( &buf ); + // write the current serverId so the server + // can tell if this is from the current gameState + MSG_WriteLong( &buf, cl.serverId ); + + // write the last message we received, which can + // be used for delta compression, and is also used + // to tell if we dropped a gamestate + MSG_WriteLong( &buf, clc.serverMessageSequence ); + + // write the last reliable message we received + MSG_WriteLong( &buf, clc.serverCommandSequence ); + + // write any unacknowledged clientCommands + for ( i = clc.reliableAcknowledge + 1 ; i <= clc.reliableSequence ; i++ ) { + MSG_WriteByte( &buf, clc_clientCommand ); + MSG_WriteLong( &buf, i ); + MSG_WriteString( &buf, clc.reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] ); + } + + // we want to send all the usercmds that were generated in the last + // few packet, so even if a couple packets are dropped in a row, + // all the cmds will make it to the server + if ( cl_packetdup->integer < 0 ) { + Cvar_Set( "cl_packetdup", "0" ); + } else if ( cl_packetdup->integer > 5 ) { + Cvar_Set( "cl_packetdup", "5" ); + } + oldPacketNum = (clc.netchan.outgoingSequence - 1 - cl_packetdup->integer) & PACKET_MASK; + count = cl.cmdNumber - cl.outPackets[ oldPacketNum ].p_cmdNumber; + if ( count > MAX_PACKET_USERCMDS ) { + count = MAX_PACKET_USERCMDS; + Com_Printf("MAX_PACKET_USERCMDS\n"); + } + +#ifdef USE_VOIP + if (clc.voipOutgoingDataSize > 0) { // only send if data. + // Move cl_voipSendTarget from a string to the bitmasks if needed. + if (cl_voipSendTarget->modified) { + char buffer[32]; + const char *target = cl_voipSendTarget->string; + + if (Q_stricmp(target, "attacker") == 0) { + int player = VM_Call( cgvm, CG_LAST_ATTACKER ); + Com_sprintf(buffer, sizeof (buffer), "%d", player); + target = buffer; + } else if (Q_stricmp(target, "crosshair") == 0) { + int player = VM_Call( cgvm, CG_CROSSHAIR_PLAYER ); + Com_sprintf(buffer, sizeof (buffer), "%d", player); + target = buffer; + } + + if ((*target == '\0') || (Q_stricmp(target, "all") == 0)) { + const int all = 0x7FFFFFFF; + clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = all; + } else if (Q_stricmp(target, "none") == 0) { + clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0; + } else { + const char *ptr = target; + clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0; + do { + if ((*ptr == ',') || (*ptr == '\0')) { + const int val = atoi(target); + target = ptr + 1; + if ((val >= 0) && (val < 31)) { + clc.voipTarget1 |= (1 << (val-0)); + } else if ((val >= 31) && (val < 62)) { + clc.voipTarget2 |= (1 << (val-31)); + } else if ((val >= 62) && (val < 93)) { + clc.voipTarget3 |= (1 << (val-62)); + } + } + } while (*(ptr++)); + } + cl_voipSendTarget->modified = qfalse; + } + + MSG_WriteByte (&buf, clc_EOF); // placate legacy servers. + MSG_WriteByte (&buf, clc_extension); + MSG_WriteByte (&buf, clc_voip); + MSG_WriteByte (&buf, clc.voipOutgoingGeneration); + MSG_WriteLong (&buf, clc.voipOutgoingSequence); + MSG_WriteByte (&buf, clc.voipOutgoingDataFrames); + MSG_WriteLong (&buf, clc.voipTarget1); + MSG_WriteLong (&buf, clc.voipTarget2); + MSG_WriteLong (&buf, clc.voipTarget3); + MSG_WriteShort (&buf, clc.voipOutgoingDataSize); + MSG_WriteData (&buf, clc.voipOutgoingData, clc.voipOutgoingDataSize); + + // If we're recording a demo, we have to fake a server packet with + // this VoIP data so it gets to disk; the server doesn't send it + // back to us, and we might as well eliminate concerns about dropped + // and misordered packets here. + if ( clc.demorecording && !clc.demowaiting ) { + const int voipSize = clc.voipOutgoingDataSize; + msg_t fakemsg; + byte fakedata[MAX_MSGLEN]; + MSG_Init (&fakemsg, fakedata, sizeof (fakedata)); + MSG_Bitstream (&fakemsg); + MSG_WriteLong (&fakemsg, clc.reliableAcknowledge); + MSG_WriteByte (&fakemsg, svc_EOF); + MSG_WriteByte (&fakemsg, svc_extension); + MSG_WriteByte (&fakemsg, svc_voip); + MSG_WriteShort (&fakemsg, clc.clientNum); + MSG_WriteByte (&fakemsg, clc.voipOutgoingGeneration); + MSG_WriteLong (&fakemsg, clc.voipOutgoingSequence); + MSG_WriteByte (&fakemsg, clc.voipOutgoingDataFrames); + MSG_WriteShort (&fakemsg, clc.voipOutgoingDataSize ); + MSG_WriteData (&fakemsg, clc.voipOutgoingData, voipSize); + MSG_WriteByte (&fakemsg, svc_EOF); + CL_WriteDemoMessage (&fakemsg, 0); + } + + clc.voipOutgoingSequence += clc.voipOutgoingDataFrames; + clc.voipOutgoingDataSize = 0; + clc.voipOutgoingDataFrames = 0; + } else +#endif + + if ( count >= 1 ) { + if ( cl_showSend->integer ) { + Com_Printf( "(%i)", count ); + } + + // begin a client move command + if ( cl_nodelta->integer || !cl.snap.valid || clc.demowaiting + || clc.serverMessageSequence != cl.snap.messageNum ) { + MSG_WriteByte (&buf, clc_moveNoDelta); + } else { + MSG_WriteByte (&buf, clc_move); + } + + // write the command count + MSG_WriteByte( &buf, count ); + + // use the checksum feed in the key + key = clc.checksumFeed; + // also use the message acknowledge + key ^= clc.serverMessageSequence; + // also use the last acknowledged server command in the key + key ^= Com_HashKey(clc.serverCommands[ clc.serverCommandSequence & (MAX_RELIABLE_COMMANDS-1) ], 32); + + // write all the commands, including the predicted command + for ( i = 0 ; i < count ; i++ ) { + j = (cl.cmdNumber - count + i + 1) & CMD_MASK; + cmd = &cl.cmds[j]; + MSG_WriteDeltaUsercmdKey (&buf, key, oldcmd, cmd); + oldcmd = cmd; + } + } + + // + // deliver the message + // + packetNum = clc.netchan.outgoingSequence & PACKET_MASK; + cl.outPackets[ packetNum ].p_realtime = cls.realtime; + cl.outPackets[ packetNum ].p_serverTime = oldcmd->serverTime; + cl.outPackets[ packetNum ].p_cmdNumber = cl.cmdNumber; + clc.lastPacketSentTime = cls.realtime; + + if ( cl_showSend->integer ) { + Com_Printf( "%i ", buf.cursize ); + } + + CL_Netchan_Transmit (&clc.netchan, &buf); + + // clients never really should have messages large enough + // to fragment, but in case they do, fire them all off + // at once + // TTimo: this causes a packet burst, which is bad karma for winsock + // added a WARNING message, we'll see if there are legit situations where this happens + while ( clc.netchan.unsentFragments ) { + Com_DPrintf( "WARNING: #462 unsent fragments (not supposed to happen!)\n" ); + CL_Netchan_TransmitNextFragment( &clc.netchan ); + } +} + +/* +================= +CL_SendCmd + +Called every frame to builds and sends a command packet to the server. +================= +*/ +void CL_SendCmd( void ) { + // don't send any message if not connected + if ( cls.state < CA_CONNECTED ) { + return; + } + + // don't send commands if paused + if ( com_sv_running->integer && sv_paused->integer && cl_paused->integer ) { + return; + } + + // we create commands even if a demo is playing, + CL_CreateNewCommands(); + + // don't send a packet if the last packet was sent too recently + if ( !CL_ReadyToSendPacket() ) { + if ( cl_showSend->integer ) { + Com_Printf( ". " ); + } + return; + } + + CL_WritePacket(); +} + +/* +============ +CL_InitInput +============ +*/ +void CL_InitInput( void ) { + Cmd_AddCommand ("centerview",IN_CenterView); + + Cmd_AddCommand ("+moveup",IN_UpDown); + Cmd_AddCommand ("-moveup",IN_UpUp); + Cmd_AddCommand ("+movedown",IN_DownDown); + Cmd_AddCommand ("-movedown",IN_DownUp); + Cmd_AddCommand ("+left",IN_LeftDown); + Cmd_AddCommand ("-left",IN_LeftUp); + Cmd_AddCommand ("+right",IN_RightDown); + Cmd_AddCommand ("-right",IN_RightUp); + Cmd_AddCommand ("+forward",IN_ForwardDown); + Cmd_AddCommand ("-forward",IN_ForwardUp); + Cmd_AddCommand ("+back",IN_BackDown); + Cmd_AddCommand ("-back",IN_BackUp); + Cmd_AddCommand ("+lookup", IN_LookupDown); + Cmd_AddCommand ("-lookup", IN_LookupUp); + Cmd_AddCommand ("+lookdown", IN_LookdownDown); + Cmd_AddCommand ("-lookdown", IN_LookdownUp); + Cmd_AddCommand ("+strafe", IN_StrafeDown); + Cmd_AddCommand ("-strafe", IN_StrafeUp); + Cmd_AddCommand ("+moveleft", IN_MoveleftDown); + Cmd_AddCommand ("-moveleft", IN_MoveleftUp); + Cmd_AddCommand ("+moveright", IN_MoverightDown); + Cmd_AddCommand ("-moveright", IN_MoverightUp); + Cmd_AddCommand ("+speed", IN_SpeedDown); + Cmd_AddCommand ("-speed", IN_SpeedUp); + Cmd_AddCommand ("+attack", IN_Button0Down); + Cmd_AddCommand ("-attack", IN_Button0Up); + Cmd_AddCommand ("+button0", IN_Button0Down); + Cmd_AddCommand ("-button0", IN_Button0Up); + Cmd_AddCommand ("+button1", IN_Button1Down); + Cmd_AddCommand ("-button1", IN_Button1Up); + Cmd_AddCommand ("+button2", IN_Button2Down); + Cmd_AddCommand ("-button2", IN_Button2Up); + Cmd_AddCommand ("+button3", IN_Button3Down); + Cmd_AddCommand ("-button3", IN_Button3Up); + Cmd_AddCommand ("+button4", IN_Button4Down); + Cmd_AddCommand ("-button4", IN_Button4Up); + Cmd_AddCommand ("+button5", IN_Button5Down); + Cmd_AddCommand ("-button5", IN_Button5Up); + Cmd_AddCommand ("+button6", IN_Button6Down); + Cmd_AddCommand ("-button6", IN_Button6Up); + Cmd_AddCommand ("+button7", IN_Button7Down); + Cmd_AddCommand ("-button7", IN_Button7Up); + Cmd_AddCommand ("+button8", IN_Button8Down); + Cmd_AddCommand ("-button8", IN_Button8Up); + Cmd_AddCommand ("+button9", IN_Button9Down); + Cmd_AddCommand ("-button9", IN_Button9Up); + Cmd_AddCommand ("+button10", IN_Button10Down); + Cmd_AddCommand ("-button10", IN_Button10Up); + Cmd_AddCommand ("+button11", IN_Button11Down); + Cmd_AddCommand ("-button11", IN_Button11Up); + Cmd_AddCommand ("+button12", IN_Button12Down); + Cmd_AddCommand ("-button12", IN_Button12Up); + Cmd_AddCommand ("+button13", IN_Button13Down); + Cmd_AddCommand ("-button13", IN_Button13Up); + Cmd_AddCommand ("+button14", IN_Button14Down); + Cmd_AddCommand ("-button14", IN_Button14Up); + Cmd_AddCommand ("+mlook", IN_MLookDown); + Cmd_AddCommand ("-mlook", IN_MLookUp); + +#ifdef USE_VOIP + Cmd_AddCommand ("+voiprecord", IN_VoipRecordDown); + Cmd_AddCommand ("-voiprecord", IN_VoipRecordUp); +#endif + + cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0); + cl_debugMove = Cvar_Get ("cl_debugMove", "0", 0); +} diff --git a/reaction/engine/code/client/cl_keys.c b/reaction/engine/code/client/cl_keys.c new file mode 100644 index 00000000..85b3cb2f --- /dev/null +++ b/reaction/engine/code/client/cl_keys.c @@ -0,0 +1,1548 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#include "client.h" + +/* + +key up events are sent even if in console mode + +*/ + +field_t historyEditLines[COMMAND_HISTORY]; + +int nextHistoryLine; // the last line in the history buffer, not masked +int historyLine; // the line being displayed from history buffer + // will be <= nextHistoryLine + +field_t g_consoleField; +field_t chatField; +qboolean chat_team; + +int chat_playerNum; + + +qboolean key_overstrikeMode; + +int anykeydown; +qkey_t keys[MAX_KEYS]; + + +typedef struct { + char *name; + int keynum; +} keyname_t; + + +// names not in this list can either be lowercase ascii, or '0xnn' hex sequences +keyname_t keynames[] = +{ + {"TAB", K_TAB}, + {"ENTER", K_ENTER}, + {"ESCAPE", K_ESCAPE}, + {"SPACE", K_SPACE}, + {"BACKSPACE", K_BACKSPACE}, + {"UPARROW", K_UPARROW}, + {"DOWNARROW", K_DOWNARROW}, + {"LEFTARROW", K_LEFTARROW}, + {"RIGHTARROW", K_RIGHTARROW}, + + {"ALT", K_ALT}, + {"CTRL", K_CTRL}, + {"SHIFT", K_SHIFT}, + + {"COMMAND", K_COMMAND}, + + {"CAPSLOCK", K_CAPSLOCK}, + + + {"F1", K_F1}, + {"F2", K_F2}, + {"F3", K_F3}, + {"F4", K_F4}, + {"F5", K_F5}, + {"F6", K_F6}, + {"F7", K_F7}, + {"F8", K_F8}, + {"F9", K_F9}, + {"F10", K_F10}, + {"F11", K_F11}, + {"F12", K_F12}, + {"F13", K_F13}, + {"F14", K_F14}, + {"F15", K_F15}, + + {"INS", K_INS}, + {"DEL", K_DEL}, + {"PGDN", K_PGDN}, + {"PGUP", K_PGUP}, + {"HOME", K_HOME}, + {"END", K_END}, + + {"MOUSE1", K_MOUSE1}, + {"MOUSE2", K_MOUSE2}, + {"MOUSE3", K_MOUSE3}, + {"MOUSE4", K_MOUSE4}, + {"MOUSE5", K_MOUSE5}, + + {"MWHEELUP", K_MWHEELUP }, + {"MWHEELDOWN", K_MWHEELDOWN }, + + {"JOY1", K_JOY1}, + {"JOY2", K_JOY2}, + {"JOY3", K_JOY3}, + {"JOY4", K_JOY4}, + {"JOY5", K_JOY5}, + {"JOY6", K_JOY6}, + {"JOY7", K_JOY7}, + {"JOY8", K_JOY8}, + {"JOY9", K_JOY9}, + {"JOY10", K_JOY10}, + {"JOY11", K_JOY11}, + {"JOY12", K_JOY12}, + {"JOY13", K_JOY13}, + {"JOY14", K_JOY14}, + {"JOY15", K_JOY15}, + {"JOY16", K_JOY16}, + {"JOY17", K_JOY17}, + {"JOY18", K_JOY18}, + {"JOY19", K_JOY19}, + {"JOY20", K_JOY20}, + {"JOY21", K_JOY21}, + {"JOY22", K_JOY22}, + {"JOY23", K_JOY23}, + {"JOY24", K_JOY24}, + {"JOY25", K_JOY25}, + {"JOY26", K_JOY26}, + {"JOY27", K_JOY27}, + {"JOY28", K_JOY28}, + {"JOY29", K_JOY29}, + {"JOY30", K_JOY30}, + {"JOY31", K_JOY31}, + {"JOY32", K_JOY32}, + + {"AUX1", K_AUX1}, + {"AUX2", K_AUX2}, + {"AUX3", K_AUX3}, + {"AUX4", K_AUX4}, + {"AUX5", K_AUX5}, + {"AUX6", K_AUX6}, + {"AUX7", K_AUX7}, + {"AUX8", K_AUX8}, + {"AUX9", K_AUX9}, + {"AUX10", K_AUX10}, + {"AUX11", K_AUX11}, + {"AUX12", K_AUX12}, + {"AUX13", K_AUX13}, + {"AUX14", K_AUX14}, + {"AUX15", K_AUX15}, + {"AUX16", K_AUX16}, + + {"KP_HOME", K_KP_HOME }, + {"KP_UPARROW", K_KP_UPARROW }, + {"KP_PGUP", K_KP_PGUP }, + {"KP_LEFTARROW", K_KP_LEFTARROW }, + {"KP_5", K_KP_5 }, + {"KP_RIGHTARROW", K_KP_RIGHTARROW }, + {"KP_END", K_KP_END }, + {"KP_DOWNARROW", K_KP_DOWNARROW }, + {"KP_PGDN", K_KP_PGDN }, + {"KP_ENTER", K_KP_ENTER }, + {"KP_INS", K_KP_INS }, + {"KP_DEL", K_KP_DEL }, + {"KP_SLASH", K_KP_SLASH }, + {"KP_MINUS", K_KP_MINUS }, + {"KP_PLUS", K_KP_PLUS }, + {"KP_NUMLOCK", K_KP_NUMLOCK }, + {"KP_STAR", K_KP_STAR }, + {"KP_EQUALS", K_KP_EQUALS }, + + {"PAUSE", K_PAUSE}, + + {"SEMICOLON", ';'}, // because a raw semicolon seperates commands + + {"WORLD_0", K_WORLD_0}, + {"WORLD_1", K_WORLD_1}, + {"WORLD_2", K_WORLD_2}, + {"WORLD_3", K_WORLD_3}, + {"WORLD_4", K_WORLD_4}, + {"WORLD_5", K_WORLD_5}, + {"WORLD_6", K_WORLD_6}, + {"WORLD_7", K_WORLD_7}, + {"WORLD_8", K_WORLD_8}, + {"WORLD_9", K_WORLD_9}, + {"WORLD_10", K_WORLD_10}, + {"WORLD_11", K_WORLD_11}, + {"WORLD_12", K_WORLD_12}, + {"WORLD_13", K_WORLD_13}, + {"WORLD_14", K_WORLD_14}, + {"WORLD_15", K_WORLD_15}, + {"WORLD_16", K_WORLD_16}, + {"WORLD_17", K_WORLD_17}, + {"WORLD_18", K_WORLD_18}, + {"WORLD_19", K_WORLD_19}, + {"WORLD_20", K_WORLD_20}, + {"WORLD_21", K_WORLD_21}, + {"WORLD_22", K_WORLD_22}, + {"WORLD_23", K_WORLD_23}, + {"WORLD_24", K_WORLD_24}, + {"WORLD_25", K_WORLD_25}, + {"WORLD_26", K_WORLD_26}, + {"WORLD_27", K_WORLD_27}, + {"WORLD_28", K_WORLD_28}, + {"WORLD_29", K_WORLD_29}, + {"WORLD_30", K_WORLD_30}, + {"WORLD_31", K_WORLD_31}, + {"WORLD_32", K_WORLD_32}, + {"WORLD_33", K_WORLD_33}, + {"WORLD_34", K_WORLD_34}, + {"WORLD_35", K_WORLD_35}, + {"WORLD_36", K_WORLD_36}, + {"WORLD_37", K_WORLD_37}, + {"WORLD_38", K_WORLD_38}, + {"WORLD_39", K_WORLD_39}, + {"WORLD_40", K_WORLD_40}, + {"WORLD_41", K_WORLD_41}, + {"WORLD_42", K_WORLD_42}, + {"WORLD_43", K_WORLD_43}, + {"WORLD_44", K_WORLD_44}, + {"WORLD_45", K_WORLD_45}, + {"WORLD_46", K_WORLD_46}, + {"WORLD_47", K_WORLD_47}, + {"WORLD_48", K_WORLD_48}, + {"WORLD_49", K_WORLD_49}, + {"WORLD_50", K_WORLD_50}, + {"WORLD_51", K_WORLD_51}, + {"WORLD_52", K_WORLD_52}, + {"WORLD_53", K_WORLD_53}, + {"WORLD_54", K_WORLD_54}, + {"WORLD_55", K_WORLD_55}, + {"WORLD_56", K_WORLD_56}, + {"WORLD_57", K_WORLD_57}, + {"WORLD_58", K_WORLD_58}, + {"WORLD_59", K_WORLD_59}, + {"WORLD_60", K_WORLD_60}, + {"WORLD_61", K_WORLD_61}, + {"WORLD_62", K_WORLD_62}, + {"WORLD_63", K_WORLD_63}, + {"WORLD_64", K_WORLD_64}, + {"WORLD_65", K_WORLD_65}, + {"WORLD_66", K_WORLD_66}, + {"WORLD_67", K_WORLD_67}, + {"WORLD_68", K_WORLD_68}, + {"WORLD_69", K_WORLD_69}, + {"WORLD_70", K_WORLD_70}, + {"WORLD_71", K_WORLD_71}, + {"WORLD_72", K_WORLD_72}, + {"WORLD_73", K_WORLD_73}, + {"WORLD_74", K_WORLD_74}, + {"WORLD_75", K_WORLD_75}, + {"WORLD_76", K_WORLD_76}, + {"WORLD_77", K_WORLD_77}, + {"WORLD_78", K_WORLD_78}, + {"WORLD_79", K_WORLD_79}, + {"WORLD_80", K_WORLD_80}, + {"WORLD_81", K_WORLD_81}, + {"WORLD_82", K_WORLD_82}, + {"WORLD_83", K_WORLD_83}, + {"WORLD_84", K_WORLD_84}, + {"WORLD_85", K_WORLD_85}, + {"WORLD_86", K_WORLD_86}, + {"WORLD_87", K_WORLD_87}, + {"WORLD_88", K_WORLD_88}, + {"WORLD_89", K_WORLD_89}, + {"WORLD_90", K_WORLD_90}, + {"WORLD_91", K_WORLD_91}, + {"WORLD_92", K_WORLD_92}, + {"WORLD_93", K_WORLD_93}, + {"WORLD_94", K_WORLD_94}, + {"WORLD_95", K_WORLD_95}, + + {"WINDOWS", K_SUPER}, + {"COMPOSE", K_COMPOSE}, + {"MODE", K_MODE}, + {"HELP", K_HELP}, + {"PRINT", K_PRINT}, + {"SYSREQ", K_SYSREQ}, + {"SCROLLOCK", K_SCROLLOCK }, + {"BREAK", K_BREAK}, + {"MENU", K_MENU}, + {"POWER", K_POWER}, + {"EURO", K_EURO}, + {"UNDO", K_UNDO}, + + {NULL,0} +}; + +/* +============================================================================= + +EDIT FIELDS + +============================================================================= +*/ + + +/* +=================== +Field_Draw + +Handles horizontal scrolling and cursor blinking +x, y, and width are in pixels +=================== +*/ +void Field_VariableSizeDraw( field_t *edit, int x, int y, int width, int size, qboolean showCursor, + qboolean noColorEscape ) { + int len; + int drawLen; + int prestep; + int cursorChar; + char str[MAX_STRING_CHARS]; + int i; + + drawLen = edit->widthInChars - 1; // - 1 so there is always a space for the cursor + len = strlen( edit->buffer ); + + // guarantee that cursor will be visible + if ( len <= drawLen ) { + prestep = 0; + } else { + if ( edit->scroll + drawLen > len ) { + edit->scroll = len - drawLen; + if ( edit->scroll < 0 ) { + edit->scroll = 0; + } + } + prestep = edit->scroll; + } + + if ( prestep + drawLen > len ) { + drawLen = len - prestep; + } + + // extract characters from the field at + if ( drawLen >= MAX_STRING_CHARS ) { + Com_Error( ERR_DROP, "drawLen >= MAX_STRING_CHARS" ); + } + + Com_Memcpy( str, edit->buffer + prestep, drawLen ); + str[ drawLen ] = 0; + + // draw it + if ( size == SMALLCHAR_WIDTH ) { + float color[4]; + + color[0] = color[1] = color[2] = color[3] = 1.0; + SCR_DrawSmallStringExt( x, y, str, color, qfalse, noColorEscape ); + } else { + // draw big string with drop shadow + SCR_DrawBigString( x, y, str, 1.0, noColorEscape ); + } + + // draw the cursor + if ( showCursor ) { + if ( (int)( cls.realtime >> 8 ) & 1 ) { + return; // off blink + } + + if ( key_overstrikeMode ) { + cursorChar = 11; + } else { + cursorChar = 10; + } + + i = drawLen - strlen( str ); + + if ( size == SMALLCHAR_WIDTH ) { + SCR_DrawSmallChar( x + ( edit->cursor - prestep - i ) * size, y, cursorChar ); + } else { + str[0] = cursorChar; + str[1] = 0; + SCR_DrawBigString( x + ( edit->cursor - prestep - i ) * size, y, str, 1.0, qfalse ); + + } + } +} + +void Field_Draw( field_t *edit, int x, int y, int width, qboolean showCursor, qboolean noColorEscape ) +{ + Field_VariableSizeDraw( edit, x, y, width, SMALLCHAR_WIDTH, showCursor, noColorEscape ); +} + +void Field_BigDraw( field_t *edit, int x, int y, int width, qboolean showCursor, qboolean noColorEscape ) +{ + Field_VariableSizeDraw( edit, x, y, width, BIGCHAR_WIDTH, showCursor, noColorEscape ); +} + +/* +================ +Field_Paste +================ +*/ +void Field_Paste( field_t *edit ) { + char *cbd; + int pasteLen, i; + + cbd = Sys_GetClipboardData(); + + if ( !cbd ) { + return; + } + + // send as if typed, so insert / overstrike works properly + pasteLen = strlen( cbd ); + for ( i = 0 ; i < pasteLen ; i++ ) { + Field_CharEvent( edit, cbd[i] ); + } + + Z_Free( cbd ); +} + +/* +================= +Field_KeyDownEvent + +Performs the basic line editing functions for the console, +in-game talk, and menu fields + +Key events are used for non-printable characters, others are gotten from char events. +================= +*/ +void Field_KeyDownEvent( field_t *edit, int key ) { + int len; + + // shift-insert is paste + if ( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && keys[K_SHIFT].down ) { + Field_Paste( edit ); + return; + } + + key = tolower( key ); + len = strlen( edit->buffer ); + + switch ( key ) { + case K_DEL: + if ( edit->cursor < len ) { + memmove( edit->buffer + edit->cursor, + edit->buffer + edit->cursor + 1, len - edit->cursor ); + } + break; + + case K_RIGHTARROW: + if ( edit->cursor < len ) { + edit->cursor++; + } + break; + + case K_LEFTARROW: + if ( edit->cursor > 0 ) { + edit->cursor--; + } + break; + + case K_HOME: + edit->cursor = 0; + break; + + case K_END: + edit->cursor = len; + break; + + case K_INS: + key_overstrikeMode = !key_overstrikeMode; + break; + + default: + break; + } + + // Change scroll if cursor is no longer visible + if ( edit->cursor < edit->scroll ) { + edit->scroll = edit->cursor; + } else if ( edit->cursor >= edit->scroll + edit->widthInChars && edit->cursor <= len ) { + edit->scroll = edit->cursor - edit->widthInChars + 1; + } +} + +/* +================== +Field_CharEvent +================== +*/ +void Field_CharEvent( field_t *edit, int ch ) { + int len; + + if ( ch == 'v' - 'a' + 1 ) { // ctrl-v is paste + Field_Paste( edit ); + return; + } + + if ( ch == 'c' - 'a' + 1 ) { // ctrl-c clears the field + Field_Clear( edit ); + return; + } + + len = strlen( edit->buffer ); + + if ( ch == 'h' - 'a' + 1 ) { // ctrl-h is backspace + if ( edit->cursor > 0 ) { + memmove( edit->buffer + edit->cursor - 1, + edit->buffer + edit->cursor, len + 1 - edit->cursor ); + edit->cursor--; + if ( edit->cursor < edit->scroll ) + { + edit->scroll--; + } + } + return; + } + + if ( ch == 'a' - 'a' + 1 ) { // ctrl-a is home + edit->cursor = 0; + edit->scroll = 0; + return; + } + + if ( ch == 'e' - 'a' + 1 ) { // ctrl-e is end + edit->cursor = len; + edit->scroll = edit->cursor - edit->widthInChars; + return; + } + + // + // ignore any other non printable chars + // + if ( ch < 32 ) { + return; + } + + if ( key_overstrikeMode ) { + if ( edit->cursor == MAX_EDIT_LINE - 1 ) + return; + edit->buffer[edit->cursor] = ch; + edit->cursor++; + } else { // insert mode + if ( len == MAX_EDIT_LINE - 1 ) { + return; // all full + } + memmove( edit->buffer + edit->cursor + 1, + edit->buffer + edit->cursor, len + 1 - edit->cursor ); + edit->buffer[edit->cursor] = ch; + edit->cursor++; + } + + + if ( edit->cursor >= edit->widthInChars ) { + edit->scroll++; + } + + if ( edit->cursor == len + 1) { + edit->buffer[edit->cursor] = 0; + } +} + +/* +============================================================================= + +CONSOLE LINE EDITING + +============================================================================== +*/ + +/* +==================== +Console_Key + +Handles history and console scrollback +==================== +*/ +void Console_Key (int key) { + // ctrl-L clears screen + if ( key == 'l' && keys[K_CTRL].down ) { + Cbuf_AddText ("clear\n"); + return; + } + + // enter finishes the line + if ( key == K_ENTER || key == K_KP_ENTER ) { + // if not in the game explicitly prepend a slash if needed + if ( cls.state != CA_ACTIVE && g_consoleField.buffer[0] != '\\' + && g_consoleField.buffer[0] != '/' ) { + char temp[MAX_EDIT_LINE-1]; + + Q_strncpyz( temp, g_consoleField.buffer, sizeof( temp ) ); + Com_sprintf( g_consoleField.buffer, sizeof( g_consoleField.buffer ), "\\%s", temp ); + g_consoleField.cursor++; + } + + Com_Printf ( "]%s\n", g_consoleField.buffer ); + + // leading slash is an explicit command + if ( g_consoleField.buffer[0] == '\\' || g_consoleField.buffer[0] == '/' ) { + Cbuf_AddText( g_consoleField.buffer+1 ); // valid command + Cbuf_AddText ("\n"); + } else { + // other text will be chat messages + if ( !g_consoleField.buffer[0] ) { + return; // empty lines just scroll the console without adding to history + } else { + Cbuf_AddText ("cmd say "); + Cbuf_AddText( g_consoleField.buffer ); + Cbuf_AddText ("\n"); + } + } + + // copy line to history buffer + historyEditLines[nextHistoryLine % COMMAND_HISTORY] = g_consoleField; + nextHistoryLine++; + historyLine = nextHistoryLine; + + Field_Clear( &g_consoleField ); + + g_consoleField.widthInChars = g_console_field_width; + + CL_SaveConsoleHistory( ); + + if ( cls.state == CA_DISCONNECTED ) { + SCR_UpdateScreen (); // force an update, because the command + } // may take some time + return; + } + + // command completion + + if (key == K_TAB) { + Field_AutoComplete(&g_consoleField); + return; + } + + // command history (ctrl-p ctrl-n for unix style) + + if ( (key == K_MWHEELUP && keys[K_SHIFT].down) || ( key == K_UPARROW ) || ( key == K_KP_UPARROW ) || + ( ( tolower(key) == 'p' ) && keys[K_CTRL].down ) ) { + if ( nextHistoryLine - historyLine < COMMAND_HISTORY + && historyLine > 0 ) { + historyLine--; + } + g_consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ]; + return; + } + + if ( (key == K_MWHEELDOWN && keys[K_SHIFT].down) || ( key == K_DOWNARROW ) || ( key == K_KP_DOWNARROW ) || + ( ( tolower(key) == 'n' ) && keys[K_CTRL].down ) ) { + historyLine++; + if (historyLine >= nextHistoryLine) { + historyLine = nextHistoryLine; + Field_Clear( &g_consoleField ); + g_consoleField.widthInChars = g_console_field_width; + return; + } + g_consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ]; + return; + } + + // console scrolling + if ( key == K_PGUP ) { + Con_PageUp(); + return; + } + + if ( key == K_PGDN) { + Con_PageDown(); + return; + } + + if ( key == K_MWHEELUP) { //----(SA) added some mousewheel functionality to the console + Con_PageUp(); + if(keys[K_CTRL].down) { // hold to accelerate scrolling + Con_PageUp(); + Con_PageUp(); + } + return; + } + + if ( key == K_MWHEELDOWN) { //----(SA) added some mousewheel functionality to the console + Con_PageDown(); + if(keys[K_CTRL].down) { // hold to accelerate scrolling + Con_PageDown(); + Con_PageDown(); + } + return; + } + + // ctrl-home = top of console + if ( key == K_HOME && keys[K_CTRL].down ) { + Con_Top(); + return; + } + + // ctrl-end = bottom of console + if ( key == K_END && keys[K_CTRL].down ) { + Con_Bottom(); + return; + } + + // pass to the normal editline routine + Field_KeyDownEvent( &g_consoleField, key ); +} + +//============================================================================ + + +/* +================ +Message_Key + +In game talk message +================ +*/ +void Message_Key( int key ) { + + char buffer[MAX_STRING_CHARS]; + + + if (key == K_ESCAPE) { + Key_SetCatcher( Key_GetCatcher( ) & ~KEYCATCH_MESSAGE ); + Field_Clear( &chatField ); + return; + } + + if ( key == K_ENTER || key == K_KP_ENTER ) + { + if ( chatField.buffer[0] && cls.state == CA_ACTIVE ) { + if (chat_playerNum != -1 ) + + Com_sprintf( buffer, sizeof( buffer ), "tell %i \"%s\"\n", chat_playerNum, chatField.buffer ); + + else if (chat_team) + + Com_sprintf( buffer, sizeof( buffer ), "say_team \"%s\"\n", chatField.buffer ); + else + Com_sprintf( buffer, sizeof( buffer ), "say \"%s\"\n", chatField.buffer ); + + + + CL_AddReliableCommand( buffer ); + } + Key_SetCatcher( Key_GetCatcher( ) & ~KEYCATCH_MESSAGE ); + Field_Clear( &chatField ); + return; + } + + Field_KeyDownEvent( &chatField, key ); +} + +//============================================================================ + + +qboolean Key_GetOverstrikeMode( void ) { + return key_overstrikeMode; +} + + +void Key_SetOverstrikeMode( qboolean state ) { + key_overstrikeMode = state; +} + + +/* +=================== +Key_IsDown +=================== +*/ +qboolean Key_IsDown( int keynum ) { + if ( keynum < 0 || keynum >= MAX_KEYS ) { + return qfalse; + } + + return keys[keynum].down; +} + +/* +=================== +Key_StringToKeynum + +Returns a key number to be used to index keys[] by looking at +the given string. Single ascii characters return themselves, while +the K_* names are matched up. + +0x11 will be interpreted as raw hex, which will allow new controlers + +to be configured even if they don't have defined names. +=================== +*/ +int Key_StringToKeynum( char *str ) { + keyname_t *kn; + + if ( !str || !str[0] ) { + return -1; + } + if ( !str[1] ) { + return str[0]; + } + + // check for hex code + if ( strlen( str ) == 4 ) { + int n = Com_HexStrToInt( str ); + + if ( n >= 0 ) { + return n; + } + } + + // scan for a text match + for ( kn=keynames ; kn->name ; kn++ ) { + if ( !Q_stricmp( str,kn->name ) ) + return kn->keynum; + } + + return -1; +} + +/* +=================== +Key_KeynumToString + +Returns a string (either a single ascii char, a K_* name, or a 0x11 hex string) for the +given keynum. +=================== +*/ +char *Key_KeynumToString( int keynum ) { + keyname_t *kn; + static char tinystr[5]; + int i, j; + + if ( keynum == -1 ) { + return ""; + } + + if ( keynum < 0 || keynum >= MAX_KEYS ) { + return ""; + } + + // check for printable ascii (don't use quote) + if ( keynum > 32 && keynum < 127 && keynum != '"' && keynum != ';' ) { + tinystr[0] = keynum; + tinystr[1] = 0; + return tinystr; + } + + // check for a key string + for ( kn=keynames ; kn->name ; kn++ ) { + if (keynum == kn->keynum) { + return kn->name; + } + } + + // make a hex string + i = keynum >> 4; + j = keynum & 15; + + tinystr[0] = '0'; + tinystr[1] = 'x'; + tinystr[2] = i > 9 ? i - 10 + 'a' : i + '0'; + tinystr[3] = j > 9 ? j - 10 + 'a' : j + '0'; + tinystr[4] = 0; + + return tinystr; +} + + +/* +=================== +Key_SetBinding +=================== +*/ +void Key_SetBinding( int keynum, const char *binding ) { + if ( keynum < 0 || keynum >= MAX_KEYS ) { + return; + } + + // free old bindings + if ( keys[ keynum ].binding ) { + Z_Free( keys[ keynum ].binding ); + } + + // allocate memory for new binding + keys[keynum].binding = CopyString( binding ); + + // consider this like modifying an archived cvar, so the + // file write will be triggered at the next oportunity + cvar_modifiedFlags |= CVAR_ARCHIVE; +} + + +/* +=================== +Key_GetBinding +=================== +*/ +char *Key_GetBinding( int keynum ) { + if ( keynum < 0 || keynum >= MAX_KEYS ) { + return ""; + } + + return keys[ keynum ].binding; +} + +/* +=================== +Key_GetKey +=================== +*/ + +int Key_GetKey(const char *binding) { + int i; + + if (binding) { + for (i=0 ; i < MAX_KEYS ; i++) { + if (keys[i].binding && Q_stricmp(binding, keys[i].binding) == 0) { + return i; + } + } + } + return -1; +} + +/* +=================== +Key_Unbind_f +=================== +*/ +void Key_Unbind_f (void) +{ + int b; + + if (Cmd_Argc() != 2) + { + Com_Printf ("unbind : remove commands from a key\n"); + return; + } + + b = Key_StringToKeynum (Cmd_Argv(1)); + if (b==-1) + { + Com_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); + return; + } + + Key_SetBinding (b, ""); +} + +/* +=================== +Key_Unbindall_f +=================== +*/ +void Key_Unbindall_f (void) +{ + int i; + + for (i=0 ; i < MAX_KEYS; i++) + if (keys[i].binding) + Key_SetBinding (i, ""); +} + + +/* +=================== +Key_Bind_f +=================== +*/ +void Key_Bind_f (void) +{ + int i, c, b; + char cmd[1024]; + + c = Cmd_Argc(); + + if (c < 2) + { + Com_Printf ("bind [command] : attach a command to a key\n"); + return; + } + b = Key_StringToKeynum (Cmd_Argv(1)); + if (b==-1) + { + Com_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); + return; + } + + if (c == 2) + { + if (keys[b].binding) + Com_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keys[b].binding ); + else + Com_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) ); + return; + } + +// copy the rest of the command line + cmd[0] = 0; // start out with a null string + for (i=2 ; i< c ; i++) + { + strcat (cmd, Cmd_Argv(i)); + if (i != (c-1)) + strcat (cmd, " "); + } + + Key_SetBinding (b, cmd); +} + +/* +============ +Key_WriteBindings + +Writes lines containing "bind key value" +============ +*/ +void Key_WriteBindings( fileHandle_t f ) { + int i; + + FS_Printf (f, "unbindall\n" ); + + for (i=0 ; i args ) + Field_CompleteKeyname( ); + } +} + +/* +==================== +Key_CompleteBind +==================== +*/ +static void Key_CompleteBind( char *args, int argNum ) +{ + char *p; + + if( argNum == 2 ) + { + // Skip "bind " + p = Com_SkipTokens( args, 1, " " ); + + if( p > args ) + Field_CompleteKeyname( ); + } + else if( argNum >= 3 ) + { + // Skip "bind " + p = Com_SkipTokens( args, 2, " " ); + + if( p > args ) + Field_CompleteCommand( p, qtrue, qtrue ); + } +} + +/* +=================== +CL_InitKeyCommands +=================== +*/ +void CL_InitKeyCommands( void ) { + // register our functions + Cmd_AddCommand ("bind",Key_Bind_f); + Cmd_SetCommandCompletionFunc( "bind", Key_CompleteBind ); + Cmd_AddCommand ("unbind",Key_Unbind_f); + Cmd_SetCommandCompletionFunc( "unbind", Key_CompleteUnbind ); + Cmd_AddCommand ("unbindall",Key_Unbindall_f); + Cmd_AddCommand ("bindlist",Key_Bindlist_f); +} + +/* +=================== +CL_AddKeyUpCommands +=================== +*/ +void CL_AddKeyUpCommands( int key, char *kb, unsigned time) { + int i; + char button[1024], *buttonPtr; + char cmd[1024]; + qboolean keyevent; + + if ( !kb ) { + return; + } + keyevent = qfalse; + buttonPtr = button; + for ( i = 0; ; i++ ) { + if ( kb[i] == ';' || !kb[i] ) { + *buttonPtr = '\0'; + if ( button[0] == '+') { + // button commands add keynum and time as parms so that multiple + // sources can be discriminated and subframe corrected + Com_sprintf (cmd, sizeof(cmd), "-%s %i %i\n", button+1, key, time); + Cbuf_AddText (cmd); + keyevent = qtrue; + } else { + if (keyevent) { + // down-only command + Cbuf_AddText (button); + Cbuf_AddText ("\n"); + } + } + buttonPtr = button; + while ( (kb[i] <= ' ' || kb[i] == ';') && kb[i] != 0 ) { + i++; + } + } + *buttonPtr++ = kb[i]; + if ( !kb[i] ) { + break; + } + } +} + +/* +=================== +CL_KeyEvent + +Called by the system for both key up and key down events +=================== +*/ +void CL_KeyEvent (int key, qboolean down, unsigned time) { + char *kb; + char cmd[1024]; + + // update auto-repeat status and BUTTON_ANY status + keys[key].down = down; + + if (down) { + keys[key].repeats++; + if ( keys[key].repeats == 1) { + anykeydown++; + } + } else { + keys[key].repeats = 0; + anykeydown--; + if (anykeydown < 0) { + anykeydown = 0; + } + } + + if (key == K_ENTER) + { + if (down) + { + if (keys[K_ALT].down) + { + Cvar_SetValue( "r_fullscreen", + !Cvar_VariableIntegerValue( "r_fullscreen" ) ); + return; + } + } + } + + // console key is hardcoded, so the user can never unbind it + if (key == K_CONSOLE || + ( key == K_ESCAPE && keys[K_SHIFT].down ) ) { + if (!down) { + return; + } + Con_ToggleConsole_f (); + Key_ClearStates (); + return; + } + + + // keys can still be used for bound actions + if ( down && ( key < 128 || key == K_MOUSE1 ) && + ( clc.demoplaying || cls.state == CA_CINEMATIC ) && Key_GetCatcher( ) == 0 ) { + + if (Cvar_VariableValue ("com_cameraMode") == 0) { + Cvar_Set ("nextdemo",""); + key = K_ESCAPE; + } + } + + + // escape is always handled special + if ( key == K_ESCAPE && down ) { + if ( Key_GetCatcher( ) & KEYCATCH_MESSAGE ) { + // clear message mode + Message_Key( key ); + return; + } + + // escape always gets out of CGAME stuff + if (Key_GetCatcher( ) & KEYCATCH_CGAME) { + Key_SetCatcher( Key_GetCatcher( ) & ~KEYCATCH_CGAME ); + VM_Call (cgvm, CG_EVENT_HANDLING, CGAME_EVENT_NONE); + return; + } + + if ( !( Key_GetCatcher( ) & KEYCATCH_UI ) ) { + if ( cls.state == CA_ACTIVE && !clc.demoplaying ) { + VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_INGAME ); + } + else if ( cls.state != CA_DISCONNECTED ) { + CL_Disconnect_f(); + S_StopAllSounds(); + VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN ); + } + return; + } + + VM_Call( uivm, UI_KEY_EVENT, key, down ); + return; + } + + // + // key up events only perform actions if the game key binding is + // a button command (leading + sign). These will be processed even in + // console mode and menu mode, to keep the character from continuing + // an action started before a mode switch. + // + if (!down ) { + if ( cls.state != CA_DISCONNECTED ) { + kb = keys[key].binding; + + CL_AddKeyUpCommands( key, kb, time ); + } + + if ( Key_GetCatcher( ) & KEYCATCH_UI && uivm ) { + VM_Call( uivm, UI_KEY_EVENT, key, down ); + } else if ( Key_GetCatcher( ) & KEYCATCH_CGAME && cgvm ) { + VM_Call( cgvm, CG_KEY_EVENT, key, down ); + } + + return; + } + + + // distribute the key down event to the apropriate handler + if ( Key_GetCatcher( ) & KEYCATCH_CONSOLE ) { + Console_Key( key ); + } else if ( Key_GetCatcher( ) & KEYCATCH_UI ) { + if ( uivm ) { + VM_Call( uivm, UI_KEY_EVENT, key, down ); + } + } else if ( Key_GetCatcher( ) & KEYCATCH_CGAME ) { + if ( cgvm ) { + VM_Call( cgvm, CG_KEY_EVENT, key, down ); + } + } else if ( Key_GetCatcher( ) & KEYCATCH_MESSAGE ) { + Message_Key( key ); + } else if ( cls.state == CA_DISCONNECTED ) { + Console_Key( key ); + } else { + // send the bound action + kb = keys[key].binding; + if ( !kb ) { + if (key >= 200) { + Com_Printf ("%s is unbound, use controls menu to set.\n" + , Key_KeynumToString( key ) ); + } + } else if (kb[0] == '+') { + int i; + char button[1024], *buttonPtr; + buttonPtr = button; + for ( i = 0; ; i++ ) { + if ( kb[i] == ';' || !kb[i] ) { + *buttonPtr = '\0'; + if ( button[0] == '+') { + // button commands add keynum and time as parms so that multiple + // sources can be discriminated and subframe corrected + Com_sprintf (cmd, sizeof(cmd), "%s %i %i\n", button, key, time); + Cbuf_AddText (cmd); + } else { + // down-only command + Cbuf_AddText (button); + Cbuf_AddText ("\n"); + } + buttonPtr = button; + while ( (kb[i] <= ' ' || kb[i] == ';') && kb[i] != 0 ) { + i++; + } + } + *buttonPtr++ = kb[i]; + if ( !kb[i] ) { + break; + } + } + } else { + // down-only command + Cbuf_AddText (kb); + Cbuf_AddText ("\n"); + } + } +} + + +/* +=================== +CL_CharEvent + +Normal keyboard characters, already shifted / capslocked / etc +=================== +*/ +void CL_CharEvent( int key ) { + // delete is not a printable character and is + // otherwise handled by Field_KeyDownEvent + if ( key == 127 ) { + return; + } + + // distribute the key down event to the apropriate handler + if ( Key_GetCatcher( ) & KEYCATCH_CONSOLE ) + { + Field_CharEvent( &g_consoleField, key ); + } + else if ( Key_GetCatcher( ) & KEYCATCH_UI ) + { + VM_Call( uivm, UI_KEY_EVENT, key | K_CHAR_FLAG, qtrue ); + } + else if ( Key_GetCatcher( ) & KEYCATCH_MESSAGE ) + { + Field_CharEvent( &chatField, key ); + } + else if ( cls.state == CA_DISCONNECTED ) + { + Field_CharEvent( &g_consoleField, key ); + } +} + + +/* +=================== +Key_ClearStates +=================== +*/ +void Key_ClearStates (void) +{ + int i; + + anykeydown = 0; + + for ( i=0 ; i < MAX_KEYS ; i++ ) { + if ( keys[i].down ) { + CL_KeyEvent( i, qfalse, 0 ); + + } + keys[i].down = 0; + keys[i].repeats = 0; + } +} + +static int keyCatchers = 0; + +/* +==================== +Key_GetCatcher +==================== +*/ +int Key_GetCatcher( void ) { + return keyCatchers; +} + +/* +==================== +Key_SetCatcher +==================== +*/ +void Key_SetCatcher( int catcher ) { + // If the catcher state is changing, clear all key states + if( catcher != keyCatchers ) + Key_ClearStates( ); + + keyCatchers = catcher; +} + +// This must not exceed MAX_CMD_LINE +#define MAX_CONSOLE_SAVE_BUFFER 1024 +#define CONSOLE_HISTORY_FILE "rq3history" +static char consoleSaveBuffer[ MAX_CONSOLE_SAVE_BUFFER ]; +static int consoleSaveBufferSize = 0; + +/* +================ +CL_LoadConsoleHistory + +Load the console history from cl_consoleHistory +================ +*/ +void CL_LoadConsoleHistory( void ) +{ + char *token, *text_p; + int i, numChars, numLines = 0; + fileHandle_t f; + + consoleSaveBufferSize = FS_FOpenFileRead( CONSOLE_HISTORY_FILE, &f, qfalse ); + if( !f ) + { + Com_Printf( "Couldn't read %s.\n", CONSOLE_HISTORY_FILE ); + return; + } + + if( consoleSaveBufferSize <= MAX_CONSOLE_SAVE_BUFFER && + FS_Read( consoleSaveBuffer, consoleSaveBufferSize, f ) == consoleSaveBufferSize ) + { + text_p = consoleSaveBuffer; + + for( i = COMMAND_HISTORY - 1; i >= 0; i-- ) + { + if( !*( token = COM_Parse( &text_p ) ) ) + break; + + historyEditLines[ i ].cursor = atoi( token ); + + if( !*( token = COM_Parse( &text_p ) ) ) + break; + + historyEditLines[ i ].scroll = atoi( token ); + + if( !*( token = COM_Parse( &text_p ) ) ) + break; + + numChars = atoi( token ); + text_p++; + if( numChars > ( strlen( consoleSaveBuffer ) - ( text_p - consoleSaveBuffer ) ) ) + { + Com_DPrintf( S_COLOR_YELLOW "WARNING: probable corrupt history\n" ); + break; + } + Com_Memcpy( historyEditLines[ i ].buffer, + text_p, numChars ); + historyEditLines[ i ].buffer[ numChars ] = '\0'; + text_p += numChars; + + numLines++; + } + + memmove( &historyEditLines[ 0 ], &historyEditLines[ i + 1 ], + numLines * sizeof( field_t ) ); + for( i = numLines; i < COMMAND_HISTORY; i++ ) + Field_Clear( &historyEditLines[ i ] ); + + historyLine = nextHistoryLine = numLines; + } + else + Com_Printf( "Couldn't read %s.\n", CONSOLE_HISTORY_FILE ); + + FS_FCloseFile( f ); +} + +/* +================ +CL_SaveConsoleHistory + +Save the console history into the cvar cl_consoleHistory +so that it persists across invocations of q3 +================ +*/ +void CL_SaveConsoleHistory( void ) +{ + int i; + int lineLength, saveBufferLength, additionalLength; + fileHandle_t f; + + consoleSaveBuffer[ 0 ] = '\0'; + + i = ( nextHistoryLine - 1 ) % COMMAND_HISTORY; + do + { + if( historyEditLines[ i ].buffer[ 0 ] ) + { + lineLength = strlen( historyEditLines[ i ].buffer ); + saveBufferLength = strlen( consoleSaveBuffer ); + + //ICK + additionalLength = lineLength + strlen( "999 999 999 " ); + + if( saveBufferLength + additionalLength < MAX_CONSOLE_SAVE_BUFFER ) + { + Q_strcat( consoleSaveBuffer, MAX_CONSOLE_SAVE_BUFFER, + va( "%d %d %d %s ", + historyEditLines[ i ].cursor, + historyEditLines[ i ].scroll, + lineLength, + historyEditLines[ i ].buffer ) ); + } + else + break; + } + i = ( i - 1 + COMMAND_HISTORY ) % COMMAND_HISTORY; + } + while( i != ( nextHistoryLine - 1 ) % COMMAND_HISTORY ); + + consoleSaveBufferSize = strlen( consoleSaveBuffer ); + + f = FS_FOpenFileWrite( CONSOLE_HISTORY_FILE ); + if( !f ) + { + Com_Printf( "Couldn't write %s.\n", CONSOLE_HISTORY_FILE ); + return; + } + + if( FS_Write( consoleSaveBuffer, consoleSaveBufferSize, f ) < consoleSaveBufferSize ) + Com_Printf( "Couldn't write %s.\n", CONSOLE_HISTORY_FILE ); + + FS_FCloseFile( f ); +} diff --git a/reaction/engine/code/client/cl_main.c b/reaction/engine/code/client/cl_main.c new file mode 100644 index 00000000..b4d3d9b2 --- /dev/null +++ b/reaction/engine/code/client/cl_main.c @@ -0,0 +1,4197 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// cl_main.c -- client main loop + +#include "client.h" +#include + +#ifdef USE_MUMBLE +#include "libmumblelink.h" +#endif + +#ifdef USE_MUMBLE +cvar_t *cl_useMumble; +cvar_t *cl_mumbleScale; +#endif + +#ifdef USE_VOIP +cvar_t *cl_voipUseVAD; +cvar_t *cl_voipVADThreshold; +cvar_t *cl_voipSend; +cvar_t *cl_voipSendTarget; +cvar_t *cl_voipGainDuringCapture; +cvar_t *cl_voipCaptureMult; +cvar_t *cl_voipShowMeter; +cvar_t *cl_voip; +#endif + +cvar_t *cl_nodelta; +cvar_t *cl_debugMove; + +cvar_t *cl_noprint; +cvar_t *cl_motd; + +cvar_t *rcon_client_password; +cvar_t *rconAddress; + +cvar_t *cl_timeout; +cvar_t *cl_maxpackets; +cvar_t *cl_packetdup; +cvar_t *cl_timeNudge; +cvar_t *cl_showTimeDelta; +cvar_t *cl_freezeDemo; + +cvar_t *cl_shownet; +cvar_t *cl_showSend; +cvar_t *cl_timedemo; +cvar_t *cl_timedemoLog; +cvar_t *cl_autoRecordDemo; +cvar_t *cl_aviFrameRate; +cvar_t *cl_aviMotionJpeg; +cvar_t *cl_forceavidemo; + +cvar_t *cl_freelook; +cvar_t *cl_sensitivity; + +cvar_t *cl_mouseAccel; +cvar_t *cl_showMouseRate; + +cvar_t *m_pitch; +cvar_t *m_yaw; +cvar_t *m_forward; +cvar_t *m_side; +cvar_t *m_filter; + +cvar_t *cl_activeAction; + +cvar_t *cl_motdString; + +cvar_t *cl_allowDownload; +cvar_t *cl_conXOffset; +cvar_t *cl_inGameVideo; + +cvar_t *cl_serverStatusResendTime; +cvar_t *cl_trn; + +cvar_t *cl_lanForcePackets; + +cvar_t *cl_guidServerUniq; + +cvar_t *cl_consoleKeys; + +clientActive_t cl; +clientConnection_t clc; +clientStatic_t cls; +vm_t *cgvm; + +// Structure containing functions exported from refresh DLL +refexport_t re; + +ping_t cl_pinglist[MAX_PINGREQUESTS]; + +typedef struct serverStatus_s +{ + char string[BIG_INFO_STRING]; + netadr_t address; + int time, startTime; + qboolean pending; + qboolean print; + qboolean retrieved; +} serverStatus_t; + +serverStatus_t cl_serverStatusList[MAX_SERVERSTATUSREQUESTS]; +int serverStatusCount; + +#if defined __USEA3D && defined __A3D_GEOM + void hA3Dg_ExportRenderGeom (refexport_t *incoming_re); +#endif + +extern void SV_BotFrame( int time ); +void CL_CheckForResend( void ); +void CL_ShowIP_f(void); +void CL_ServerStatus_f(void); +void CL_ServerStatusResponse( netadr_t from, msg_t *msg ); + +/* +=============== +CL_CDDialog + +Called by Com_Error when a cd is needed +=============== +*/ +void CL_CDDialog( void ) { + cls.cddialog = qtrue; // start it next frame +} + +#ifdef USE_MUMBLE +static +void CL_UpdateMumble(void) +{ + vec3_t pos, forward, up; + float scale = cl_mumbleScale->value; + float tmp; + + if(!cl_useMumble->integer) + return; + + // !!! FIXME: not sure if this is even close to correct. + AngleVectors( cl.snap.ps.viewangles, forward, NULL, up); + + pos[0] = cl.snap.ps.origin[0] * scale; + pos[1] = cl.snap.ps.origin[2] * scale; + pos[2] = cl.snap.ps.origin[1] * scale; + + tmp = forward[1]; + forward[1] = forward[2]; + forward[2] = tmp; + + tmp = up[1]; + up[1] = up[2]; + up[2] = tmp; + + if(cl_useMumble->integer > 1) { + fprintf(stderr, "%f %f %f, %f %f %f, %f %f %f\n", + pos[0], pos[1], pos[2], + forward[0], forward[1], forward[2], + up[0], up[1], up[2]); + } + + mumble_update_coordinates(pos, forward, up); +} +#endif + + +#ifdef USE_VOIP +static +void CL_UpdateVoipIgnore(const char *idstr, qboolean ignore) +{ + if ((*idstr >= '0') && (*idstr <= '9')) { + const int id = atoi(idstr); + if ((id >= 0) && (id < MAX_CLIENTS)) { + clc.voipIgnore[id] = ignore; + CL_AddReliableCommand(va("voip %s %d", + ignore ? "ignore" : "unignore", id)); + Com_Printf("VoIP: %s ignoring player #%d\n", + ignore ? "Now" : "No longer", id); + } + } +} + +static +void CL_UpdateVoipGain(const char *idstr, float gain) +{ + if ((*idstr >= '0') && (*idstr <= '9')) { + const int id = atoi(idstr); + if (gain < 0.0f) + gain = 0.0f; + if ((id >= 0) && (id < MAX_CLIENTS)) { + clc.voipGain[id] = gain; + Com_Printf("VoIP: player #%d gain now set to %f\n", id, gain); + } + } +} + +void CL_Voip_f( void ) +{ + const char *cmd = Cmd_Argv(1); + const char *reason = NULL; + + if (cls.state != CA_ACTIVE) + reason = "Not connected to a server"; + else if (!clc.speexInitialized) + reason = "Speex not initialized"; + else if (!cl_connectedToVoipServer) + reason = "Server doesn't support VoIP"; + else if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) + reason = "running in single-player mode"; + + if (reason != NULL) { + Com_Printf("VoIP: command ignored: %s\n", reason); + return; + } + + if (strcmp(cmd, "ignore") == 0) { + CL_UpdateVoipIgnore(Cmd_Argv(2), qtrue); + } else if (strcmp(cmd, "unignore") == 0) { + CL_UpdateVoipIgnore(Cmd_Argv(2), qfalse); + } else if (strcmp(cmd, "gain") == 0) { + CL_UpdateVoipGain(Cmd_Argv(2), atof(Cmd_Argv(3))); + } else if (strcmp(cmd, "muteall") == 0) { + Com_Printf("VoIP: muting incoming voice\n"); + CL_AddReliableCommand("voip muteall"); + clc.voipMuteAll = qtrue; + } else if (strcmp(cmd, "unmuteall") == 0) { + Com_Printf("VoIP: unmuting incoming voice\n"); + CL_AddReliableCommand("voip unmuteall"); + clc.voipMuteAll = qfalse; + } +} + + +static +void CL_VoipNewGeneration(void) +{ + // don't have a zero generation so new clients won't match, and don't + // wrap to negative so MSG_ReadLong() doesn't "fail." + clc.voipOutgoingGeneration++; + if (clc.voipOutgoingGeneration <= 0) + clc.voipOutgoingGeneration = 1; + clc.voipPower = 0.0f; + clc.voipOutgoingSequence = 0; +} + +/* +=============== +CL_CaptureVoip + +Record more audio from the hardware if required and encode it into Speex + data for later transmission. +=============== +*/ +static +void CL_CaptureVoip(void) +{ + const float audioMult = cl_voipCaptureMult->value; + const qboolean useVad = (cl_voipUseVAD->integer != 0); + qboolean initialFrame = qfalse; + qboolean finalFrame = qfalse; + +#if USE_MUMBLE + // if we're using Mumble, don't try to handle VoIP transmission ourselves. + if (cl_useMumble->integer) + return; +#endif + + if (!clc.speexInitialized) + return; // just in case this gets called at a bad time. + + if (clc.voipOutgoingDataSize > 0) + return; // packet is pending transmission, don't record more yet. + + if (cl_voipUseVAD->modified) { + Cvar_Set("cl_voipSend", (useVad) ? "1" : "0"); + cl_voipUseVAD->modified = qfalse; + } + + if ((useVad) && (!cl_voipSend->integer)) + Cvar_Set("cl_voipSend", "1"); // lots of things reset this. + + if (cl_voipSend->modified) { + qboolean dontCapture = qfalse; + if (cls.state != CA_ACTIVE) + dontCapture = qtrue; // not connected to a server. + else if (!cl_connectedToVoipServer) + dontCapture = qtrue; // server doesn't support VoIP. + else if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) + dontCapture = qtrue; // single player game. + else if (clc.demoplaying) + dontCapture = qtrue; // playing back a demo. + else if ( cl_voip->integer == 0 ) + dontCapture = qtrue; // client has VoIP support disabled. + else if ( audioMult == 0.0f ) + dontCapture = qtrue; // basically silenced incoming audio. + + cl_voipSend->modified = qfalse; + + if (dontCapture) { + cl_voipSend->integer = 0; + return; + } + + if (cl_voipSend->integer) { + initialFrame = qtrue; + } else { + finalFrame = qtrue; + } + } + + // try to get more audio data from the sound card... + + if (initialFrame) { + float gain = cl_voipGainDuringCapture->value; + if (gain < 0.0f) gain = 0.0f; else if (gain >= 1.0f) gain = 1.0f; + S_MasterGain(cl_voipGainDuringCapture->value); + S_StartCapture(); + CL_VoipNewGeneration(); + } + + if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio? + int samples = S_AvailableCaptureSamples(); + const int mult = (finalFrame) ? 1 : 12; // 12 == 240ms of audio. + + // enough data buffered in audio hardware to process yet? + if (samples >= (clc.speexFrameSize * mult)) { + // audio capture is always MONO16 (and that's what speex wants!). + // 2048 will cover 12 uncompressed frames in narrowband mode. + static int16_t sampbuffer[2048]; + float voipPower = 0.0f; + int speexFrames = 0; + int wpos = 0; + int pos = 0; + + if (samples > (clc.speexFrameSize * 12)) + samples = (clc.speexFrameSize * 12); + + // !!! FIXME: maybe separate recording from encoding, so voipPower + // !!! FIXME: updates faster than 4Hz? + + samples -= samples % clc.speexFrameSize; + S_Capture(samples, (byte *) sampbuffer); // grab from audio card. + + // this will probably generate multiple speex packets each time. + while (samples > 0) { + int16_t *sampptr = &sampbuffer[pos]; + int i, bytes; + + // preprocess samples to remove noise... + speex_preprocess_run(clc.speexPreprocessor, sampptr); + + // check the "power" of this packet... + for (i = 0; i < clc.speexFrameSize; i++) { + const float flsamp = (float) sampptr[i]; + const float s = fabs(flsamp); + voipPower += s * s; + sampptr[i] = (int16_t) ((flsamp) * audioMult); + } + + // encode raw audio samples into Speex data... + speex_bits_reset(&clc.speexEncoderBits); + speex_encode_int(clc.speexEncoder, sampptr, + &clc.speexEncoderBits); + bytes = speex_bits_write(&clc.speexEncoderBits, + (char *) &clc.voipOutgoingData[wpos+1], + sizeof (clc.voipOutgoingData) - (wpos+1)); + assert((bytes > 0) && (bytes < 256)); + clc.voipOutgoingData[wpos] = (byte) bytes; + wpos += bytes + 1; + + // look at the data for the next packet... + pos += clc.speexFrameSize; + samples -= clc.speexFrameSize; + speexFrames++; + } + + clc.voipPower = (voipPower / (32768.0f * 32768.0f * + ((float) (clc.speexFrameSize * speexFrames)))) * + 100.0f; + + if ((useVad) && (clc.voipPower < cl_voipVADThreshold->value)) { + CL_VoipNewGeneration(); // no "talk" for at least 1/4 second. + } else { + clc.voipOutgoingDataSize = wpos; + clc.voipOutgoingDataFrames = speexFrames; + + Com_DPrintf("VoIP: Send %d frames, %d bytes, %f power\n", + speexFrames, wpos, clc.voipPower); + + #if 0 + static FILE *encio = NULL; + if (encio == NULL) encio = fopen("voip-outgoing-encoded.bin", "wb"); + if (encio != NULL) { fwrite(clc.voipOutgoingData, wpos, 1, encio); fflush(encio); } + static FILE *decio = NULL; + if (decio == NULL) decio = fopen("voip-outgoing-decoded.bin", "wb"); + if (decio != NULL) { fwrite(sampbuffer, speexFrames * clc.speexFrameSize * 2, 1, decio); fflush(decio); } + #endif + } + } + } + + // User requested we stop recording, and we've now processed the last of + // any previously-buffered data. Pause the capture device, etc. + if (finalFrame) { + S_StopCapture(); + S_MasterGain(1.0f); + clc.voipPower = 0.0f; // force this value so it doesn't linger. + } +} +#endif + +/* +======================================================================= + +CLIENT RELIABLE COMMAND COMMUNICATION + +======================================================================= +*/ + +/* +====================== +CL_AddReliableCommand + +The given command will be transmitted to the server, and is gauranteed to +not have future usercmd_t executed before it is executed +====================== +*/ +void CL_AddReliableCommand( const char *cmd ) { + int index; + + // if we would be losing an old command that hasn't been acknowledged, + // we must drop the connection + if ( clc.reliableSequence - clc.reliableAcknowledge > MAX_RELIABLE_COMMANDS ) { + Com_Error( ERR_DROP, "Client command overflow" ); + } + clc.reliableSequence++; + index = clc.reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 ); + Q_strncpyz( clc.reliableCommands[ index ], cmd, sizeof( clc.reliableCommands[ index ] ) ); +} + +/* +====================== +CL_ChangeReliableCommand +====================== +*/ +void CL_ChangeReliableCommand( void ) { + int r, index, l; + + r = clc.reliableSequence - (random() * 5); + index = clc.reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 ); + l = strlen(clc.reliableCommands[ index ]); + if ( l >= MAX_STRING_CHARS - 1 ) { + l = MAX_STRING_CHARS - 2; + } + clc.reliableCommands[ index ][ l ] = '\n'; + clc.reliableCommands[ index ][ l+1 ] = '\0'; +} + +/* +======================================================================= + +CLIENT SIDE DEMO RECORDING + +======================================================================= +*/ + +/* +==================== +CL_WriteDemoMessage + +Dumps the current net message, prefixed by the length +==================== +*/ + +void CL_WriteDemoMessage ( msg_t *msg, int headerBytes ) { + int len, swlen; + + // write the packet sequence + len = clc.serverMessageSequence; + swlen = LittleLong( len ); + FS_Write (&swlen, 4, clc.demofile); + + // skip the packet sequencing information + len = msg->cursize - headerBytes; + swlen = LittleLong(len); + FS_Write (&swlen, 4, clc.demofile); + FS_Write ( msg->data + headerBytes, len, clc.demofile ); +} + + +/* +==================== +CL_StopRecording_f + +stop recording a demo +==================== +*/ +void CL_StopRecord_f( void ) { + int len; + + if ( !clc.demorecording ) { + Com_Printf ("Not recording a demo.\n"); + return; + } + + // finish up + len = -1; + FS_Write (&len, 4, clc.demofile); + FS_Write (&len, 4, clc.demofile); + FS_FCloseFile (clc.demofile); + clc.demofile = 0; + clc.demorecording = qfalse; + clc.spDemoRecording = qfalse; + Com_Printf ("Stopped demo.\n"); +} + +/* +================== +CL_DemoFilename +================== +*/ +void CL_DemoFilename( int number, char *fileName ) { + int a,b,c,d; + + if(number < 0 || number > 9999) + number = 9999; + + a = number / 1000; + number -= a*1000; + b = number / 100; + number -= b*100; + c = number / 10; + number -= c*10; + d = number; + + Com_sprintf( fileName, MAX_OSPATH, "demo%i%i%i%i" + , a, b, c, d ); +} + +/* +==================== +CL_Record_f + +record + +Begins recording a demo from the current position +==================== +*/ +static char demoName[MAX_QPATH]; // compiler bug workaround +void CL_Record_f( void ) { + char name[MAX_OSPATH]; + byte bufData[MAX_MSGLEN]; + msg_t buf; + int i; + int len; + entityState_t *ent; + entityState_t nullstate; + char *s; + + if ( Cmd_Argc() > 2 ) { + Com_Printf ("record \n"); + return; + } + + if ( clc.demorecording ) { + if (!clc.spDemoRecording) { + Com_Printf ("Already recording.\n"); + } + return; + } + + if ( cls.state != CA_ACTIVE ) { + Com_Printf ("You must be in a level to record.\n"); + return; + } + + // sync 0 doesn't prevent recording, so not forcing it off .. everyone does g_sync 1 ; record ; g_sync 0 .. + if ( NET_IsLocalAddress( clc.serverAddress ) && !Cvar_VariableValue( "g_synchronousClients" ) ) { + Com_Printf (S_COLOR_YELLOW "WARNING: You should set 'g_synchronousClients 1' for smoother demo recording\n"); + } + + if ( Cmd_Argc() == 2 ) { + s = Cmd_Argv(1); + Q_strncpyz( demoName, s, sizeof( demoName ) ); + Com_sprintf (name, sizeof(name), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION ); + } else { + int number; + + // scan for a free demo name + for ( number = 0 ; number <= 9999 ; number++ ) { + CL_DemoFilename( number, demoName ); + Com_sprintf (name, sizeof(name), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION ); + + if (!FS_FileExists(name)) + break; // file doesn't exist + } + } + + // open the demo file + + Com_Printf ("recording to %s.\n", name); + clc.demofile = FS_FOpenFileWrite( name ); + if ( !clc.demofile ) { + Com_Printf ("ERROR: couldn't open.\n"); + return; + } + clc.demorecording = qtrue; + if (Cvar_VariableValue("ui_recordSPDemo")) { + clc.spDemoRecording = qtrue; + } else { + clc.spDemoRecording = qfalse; + } + + + Q_strncpyz( clc.demoName, demoName, sizeof( clc.demoName ) ); + + // don't start saving messages until a non-delta compressed message is received + clc.demowaiting = qtrue; + + // write out the gamestate message + MSG_Init (&buf, bufData, sizeof(bufData)); + MSG_Bitstream(&buf); + + // NOTE, MRE: all server->client messages now acknowledge + MSG_WriteLong( &buf, clc.reliableSequence ); + + MSG_WriteByte (&buf, svc_gamestate); + MSG_WriteLong (&buf, clc.serverCommandSequence ); + + // configstrings + for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { + if ( !cl.gameState.stringOffsets[i] ) { + continue; + } + s = cl.gameState.stringData + cl.gameState.stringOffsets[i]; + MSG_WriteByte (&buf, svc_configstring); + MSG_WriteShort (&buf, i); + MSG_WriteBigString (&buf, s); + } + + // baselines + Com_Memset (&nullstate, 0, sizeof(nullstate)); + for ( i = 0; i < MAX_GENTITIES ; i++ ) { + ent = &cl.entityBaselines[i]; + if ( !ent->number ) { + continue; + } + MSG_WriteByte (&buf, svc_baseline); + MSG_WriteDeltaEntity (&buf, &nullstate, ent, qtrue ); + } + + MSG_WriteByte( &buf, svc_EOF ); + + // finished writing the gamestate stuff + + // write the client num + MSG_WriteLong(&buf, clc.clientNum); + // write the checksum feed + MSG_WriteLong(&buf, clc.checksumFeed); + + // finished writing the client packet + MSG_WriteByte( &buf, svc_EOF ); + + // write it to the demo file + len = LittleLong( clc.serverMessageSequence - 1 ); + FS_Write (&len, 4, clc.demofile); + + len = LittleLong (buf.cursize); + FS_Write (&len, 4, clc.demofile); + FS_Write (buf.data, buf.cursize, clc.demofile); + + // the rest of the demo file will be copied from net messages +} + +/* +======================================================================= + +CLIENT SIDE DEMO PLAYBACK + +======================================================================= +*/ + +/* +================= +CL_DemoFrameDurationSDev +================= +*/ +static float CL_DemoFrameDurationSDev( void ) +{ + int i; + int numFrames; + float mean = 0.0f; + float variance = 0.0f; + + if( ( clc.timeDemoFrames - 1 ) > MAX_TIMEDEMO_DURATIONS ) + numFrames = MAX_TIMEDEMO_DURATIONS; + else + numFrames = clc.timeDemoFrames - 1; + + for( i = 0; i < numFrames; i++ ) + mean += clc.timeDemoDurations[ i ]; + mean /= numFrames; + + for( i = 0; i < numFrames; i++ ) + { + float x = clc.timeDemoDurations[ i ]; + + variance += ( ( x - mean ) * ( x - mean ) ); + } + variance /= numFrames; + + return sqrt( variance ); +} + +/* +================= +CL_DemoCompleted +================= +*/ +void CL_DemoCompleted( void ) +{ + char buffer[ MAX_STRING_CHARS ]; + + if( cl_timedemo && cl_timedemo->integer ) + { + int time; + + time = Sys_Milliseconds() - clc.timeDemoStart; + if( time > 0 ) + { + // Millisecond times are frame durations: + // minimum/average/maximum/std deviation + Com_sprintf( buffer, sizeof( buffer ), + "%i frames %3.1f seconds %3.1f fps %d.0/%.1f/%d.0/%.1f ms\n", + clc.timeDemoFrames, + time/1000.0, + clc.timeDemoFrames*1000.0 / time, + clc.timeDemoMinDuration, + time / (float)clc.timeDemoFrames, + clc.timeDemoMaxDuration, + CL_DemoFrameDurationSDev( ) ); + Com_Printf( "%s", buffer ); + + // Write a log of all the frame durations + if( cl_timedemoLog && strlen( cl_timedemoLog->string ) > 0 ) + { + int i; + int numFrames; + fileHandle_t f; + + if( ( clc.timeDemoFrames - 1 ) > MAX_TIMEDEMO_DURATIONS ) + numFrames = MAX_TIMEDEMO_DURATIONS; + else + numFrames = clc.timeDemoFrames - 1; + + f = FS_FOpenFileWrite( cl_timedemoLog->string ); + if( f ) + { + FS_Printf( f, "# %s", buffer ); + + for( i = 0; i < numFrames; i++ ) + FS_Printf( f, "%d\n", clc.timeDemoDurations[ i ] ); + + FS_FCloseFile( f ); + Com_Printf( "%s written\n", cl_timedemoLog->string ); + } + else + { + Com_Printf( "Couldn't open %s for writing\n", + cl_timedemoLog->string ); + } + } + } + } + + CL_Disconnect( qtrue ); + CL_NextDemo(); +} + +/* +================= +CL_ReadDemoMessage +================= +*/ +void CL_ReadDemoMessage( void ) { + int r; + msg_t buf; + byte bufData[ MAX_MSGLEN ]; + int s; + + if ( !clc.demofile ) { + CL_DemoCompleted (); + return; + } + + // get the sequence number + r = FS_Read( &s, 4, clc.demofile); + if ( r != 4 ) { + CL_DemoCompleted (); + return; + } + clc.serverMessageSequence = LittleLong( s ); + + // init the message + MSG_Init( &buf, bufData, sizeof( bufData ) ); + + // get the length + r = FS_Read (&buf.cursize, 4, clc.demofile); + if ( r != 4 ) { + CL_DemoCompleted (); + return; + } + buf.cursize = LittleLong( buf.cursize ); + if ( buf.cursize == -1 ) { + CL_DemoCompleted (); + return; + } + if ( buf.cursize > buf.maxsize ) { + Com_Error (ERR_DROP, "CL_ReadDemoMessage: demoMsglen > MAX_MSGLEN"); + } + r = FS_Read( buf.data, buf.cursize, clc.demofile ); + if ( r != buf.cursize ) { + Com_Printf( "Demo file was truncated.\n"); + CL_DemoCompleted (); + return; + } + + clc.lastPacketTime = cls.realtime; + buf.readcount = 0; + CL_ParseServerMessage( &buf ); +} + +/* +==================== +CL_WalkDemoExt +==================== +*/ +static void CL_WalkDemoExt(char *arg, char *name, int *demofile) +{ + int i = 0; + *demofile = 0; + while(demo_protocols[i]) + { + Com_sprintf (name, MAX_OSPATH, "demos/%s.dm_%d", arg, demo_protocols[i]); + FS_FOpenFileRead( name, demofile, qtrue ); + if (*demofile) + { + Com_Printf("Demo file: %s\n", name); + break; + } + else + Com_Printf("Not found: %s\n", name); + i++; + } +} + +/* +==================== +CL_CompleteDemoName +==================== +*/ +static void CL_CompleteDemoName( char *args, int argNum ) +{ + if( argNum == 2 ) + { + char demoExt[ 16 ]; + + Com_sprintf( demoExt, sizeof( demoExt ), ".dm_%d", PROTOCOL_VERSION ); + Field_CompleteFilename( "demos", demoExt, qtrue ); + } +} + +/* +==================== +CL_PlayDemo_f + +demo + +==================== +*/ +void CL_PlayDemo_f( void ) { + char name[MAX_OSPATH]; + char *arg, *ext_test; + int protocol, i; + char retry[MAX_OSPATH]; + + if (Cmd_Argc() != 2) { + Com_Printf ("playdemo \n"); + return; + } + + // make sure a local server is killed + // 2 means don't force disconnect of local client + Cvar_Set( "sv_killserver", "2" ); + + CL_Disconnect( qtrue ); + + // open the demo file + arg = Cmd_Argv(1); + + // check for an extension .dm_?? (?? is protocol) + ext_test = arg + strlen(arg) - 6; + if ((strlen(arg) > 6) && (ext_test[0] == '.') && ((ext_test[1] == 'd') || (ext_test[1] == 'D')) && ((ext_test[2] == 'm') || (ext_test[2] == 'M')) && (ext_test[3] == '_')) + { + protocol = atoi(ext_test+4); + i=0; + while(demo_protocols[i]) + { + if (demo_protocols[i] == protocol) + break; + i++; + } + if (demo_protocols[i]) + { + Com_sprintf (name, sizeof(name), "demos/%s", arg); + FS_FOpenFileRead( name, &clc.demofile, qtrue ); + } else { + Com_Printf("Protocol %d not supported for demos\n", protocol); + Q_strncpyz(retry, arg, sizeof(retry)); + retry[strlen(retry)-6] = 0; + CL_WalkDemoExt( retry, name, &clc.demofile ); + } + } else { + CL_WalkDemoExt( arg, name, &clc.demofile ); + } + + if (!clc.demofile) { + Com_Error( ERR_DROP, "couldn't open %s", name); + return; + } + Q_strncpyz( clc.demoName, Cmd_Argv(1), sizeof( clc.demoName ) ); + + Con_Close(); + + cls.state = CA_CONNECTED; + clc.demoplaying = qtrue; + Q_strncpyz( cls.servername, Cmd_Argv(1), sizeof( cls.servername ) ); + + // read demo messages until connected + while ( cls.state >= CA_CONNECTED && cls.state < CA_PRIMED ) { + CL_ReadDemoMessage(); + } + // don't get the first snapshot this frame, to prevent the long + // time from the gamestate load from messing causing a time skip + clc.firstDemoFrameSkipped = qfalse; +} + + +/* +==================== +CL_StartDemoLoop + +Closing the main menu will restart the demo loop +==================== +*/ +void CL_StartDemoLoop( void ) { + // start the demo loop again + Cbuf_AddText ("d1\n"); + Key_SetCatcher( 0 ); +} + +/* +================== +CL_NextDemo + +Called when a demo or cinematic finishes +If the "nextdemo" cvar is set, that command will be issued +================== +*/ +void CL_NextDemo( void ) { + char v[MAX_STRING_CHARS]; + + Q_strncpyz( v, Cvar_VariableString ("nextdemo"), sizeof(v) ); + v[MAX_STRING_CHARS-1] = 0; + Com_DPrintf("CL_NextDemo: %s\n", v ); + if (!v[0]) { + return; + } + + Cvar_Set ("nextdemo",""); + Cbuf_AddText (v); + Cbuf_AddText ("\n"); + Cbuf_Execute(); +} + + +//====================================================================== + +/* +===================== +CL_ShutdownAll +===================== +*/ +void CL_ShutdownAll(void) { + +#ifdef USE_CURL + CL_cURL_Shutdown(); +#endif + // clear sounds + S_DisableSounds(); + // shutdown CGame + CL_ShutdownCGame(); + // shutdown UI + CL_ShutdownUI(); + + // shutdown the renderer + if ( re.Shutdown ) { + re.Shutdown( qfalse ); // don't destroy window or context + } + + cls.uiStarted = qfalse; + cls.cgameStarted = qfalse; + cls.rendererStarted = qfalse; + cls.soundRegistered = qfalse; +} + +/* +================= +CL_FlushMemory + +Called by CL_MapLoading, CL_Connect_f, CL_PlayDemo_f, and CL_ParseGamestate the only +ways a client gets into a game +Also called by Com_Error +================= +*/ +void CL_FlushMemory( void ) { + + // shutdown all the client stuff + CL_ShutdownAll(); + + // if not running a server clear the whole hunk + if ( !com_sv_running->integer ) { + // clear the whole hunk + Hunk_Clear(); + // clear collision map data + CM_ClearMap(); + } + else { + // clear all the client data on the hunk + Hunk_ClearToMark(); + } + + CL_StartHunkUsers( qfalse ); +} + +/* +===================== +CL_MapLoading + +A local server is starting to load a map, so update the +screen to let the user know about it, then dump all client +memory on the hunk from cgame, ui, and renderer +===================== +*/ +void CL_MapLoading( void ) { + if ( com_dedicated->integer ) { + cls.state = CA_DISCONNECTED; + Key_SetCatcher( KEYCATCH_CONSOLE ); + return; + } + + if ( !com_cl_running->integer ) { + return; + } + + Con_Close(); + Key_SetCatcher( 0 ); + + // if we are already connected to the local host, stay connected + if ( cls.state >= CA_CONNECTED && !Q_stricmp( cls.servername, "localhost" ) ) { + cls.state = CA_CONNECTED; // so the connect screen is drawn + Com_Memset( cls.updateInfoString, 0, sizeof( cls.updateInfoString ) ); + Com_Memset( clc.serverMessage, 0, sizeof( clc.serverMessage ) ); + Com_Memset( &cl.gameState, 0, sizeof( cl.gameState ) ); + clc.lastPacketSentTime = -9999; + SCR_UpdateScreen(); + } else { + // clear nextmap so the cinematic shutdown doesn't execute it + Cvar_Set( "nextmap", "" ); + CL_Disconnect( qtrue ); + Q_strncpyz( cls.servername, "localhost", sizeof(cls.servername) ); + cls.state = CA_CHALLENGING; // so the connect screen is drawn + Key_SetCatcher( 0 ); + SCR_UpdateScreen(); + clc.connectTime = -RETRANSMIT_TIMEOUT; + NET_StringToAdr( cls.servername, &clc.serverAddress, NA_UNSPEC); + // we don't need a challenge on the localhost + + CL_CheckForResend(); + } +} + +/* +===================== +CL_ClearState + +Called before parsing a gamestate +===================== +*/ +void CL_ClearState (void) { + +// S_StopAllSounds(); + + Com_Memset( &cl, 0, sizeof( cl ) ); +} + +/* +==================== +CL_UpdateGUID + +update cl_guid using QKEY_FILE and optional prefix +==================== +*/ +static void CL_UpdateGUID( const char *prefix, int prefix_len ) +{ + fileHandle_t f; + int len; + + len = FS_SV_FOpenFileRead( QKEY_FILE, &f ); + FS_FCloseFile( f ); + + if( len != QKEY_SIZE ) + Cvar_Set( "cl_guid", "" ); + else + Cvar_Set( "cl_guid", Com_MD5File( QKEY_FILE, QKEY_SIZE, + prefix, prefix_len ) ); +} + + +/* +===================== +CL_Disconnect + +Called when a connection, demo, or cinematic is being terminated. +Goes from a connected state to either a menu state or a console state +Sends a disconnect message to the server +This is also called on Com_Error and Com_Quit, so it shouldn't cause any errors +===================== +*/ +void CL_Disconnect( qboolean showMainMenu ) { + if ( !com_cl_running || !com_cl_running->integer ) { + return; + } + + // shutting down the client so enter full screen ui mode + Cvar_Set("r_uiFullScreen", "1"); + + if ( clc.demorecording ) { + CL_StopRecord_f (); + } + + if (clc.download) { + FS_FCloseFile( clc.download ); + clc.download = 0; + } + *clc.downloadTempName = *clc.downloadName = 0; + Cvar_Set( "cl_downloadName", "" ); + +#ifdef USE_MUMBLE + if (cl_useMumble->integer && mumble_islinked()) { + Com_Printf("Mumble: Unlinking from Mumble application\n"); + mumble_unlink(); + } +#endif + +#ifdef USE_VOIP + if (cl_voipSend->integer) { + int tmp = cl_voipUseVAD->integer; + cl_voipUseVAD->integer = 0; // disable this for a moment. + clc.voipOutgoingDataSize = 0; // dump any pending VoIP transmission. + Cvar_Set("cl_voipSend", "0"); + CL_CaptureVoip(); // clean up any state... + cl_voipUseVAD->integer = tmp; + } + + if (clc.speexInitialized) { + int i; + speex_bits_destroy(&clc.speexEncoderBits); + speex_encoder_destroy(clc.speexEncoder); + speex_preprocess_state_destroy(clc.speexPreprocessor); + for (i = 0; i < MAX_CLIENTS; i++) { + speex_bits_destroy(&clc.speexDecoderBits[i]); + speex_decoder_destroy(clc.speexDecoder[i]); + } + } + Cmd_RemoveCommand ("voip"); +#endif + + if ( clc.demofile ) { + FS_FCloseFile( clc.demofile ); + clc.demofile = 0; + } + + if ( uivm && showMainMenu ) { + VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE ); + } + + SCR_StopCinematic (); + S_ClearSoundBuffer(); + + // send a disconnect message to the server + // send it a few times in case one is dropped + if ( cls.state >= CA_CONNECTED ) { + CL_AddReliableCommand( "disconnect" ); + CL_WritePacket(); + CL_WritePacket(); + CL_WritePacket(); + } + + CL_ClearState (); + + // wipe the client connection + Com_Memset( &clc, 0, sizeof( clc ) ); + + cls.state = CA_DISCONNECTED; + + // allow cheats locally + Cvar_Set( "sv_cheats", "1" ); + + // not connected to a pure server anymore + cl_connectedToPureServer = qfalse; + +#ifdef USE_VOIP + // not connected to voip server anymore. + cl_connectedToVoipServer = qfalse; +#endif + + // Stop recording any video + if( CL_VideoRecording( ) ) { + // Finish rendering current frame + SCR_UpdateScreen( ); + CL_CloseAVI( ); + } + CL_UpdateGUID( NULL, 0 ); +} + + +/* +=================== +CL_ForwardCommandToServer + +adds the current command line as a clientCommand +things like godmode, noclip, etc, are commands directed to the server, +so when they are typed in at the console, they will need to be forwarded. +=================== +*/ +void CL_ForwardCommandToServer( const char *string ) { + char *cmd; + + cmd = Cmd_Argv(0); + + // ignore key up commands + if ( cmd[0] == '-' ) { + return; + } + + if ( clc.demoplaying || cls.state < CA_CONNECTED || cmd[0] == '+' ) { + Com_Printf ("Unknown command \"%s" S_COLOR_WHITE "\"\n", cmd); + return; + } + + if ( Cmd_Argc() > 1 ) { + CL_AddReliableCommand( string ); + } else { + CL_AddReliableCommand( cmd ); + } +} + +/* +=================== +CL_RequestMotd + +=================== +*/ +void CL_RequestMotd( void ) { + char info[MAX_INFO_STRING]; + + if ( !cl_motd->integer ) { + return; + } + Com_Printf( "Resolving %s\n", UPDATE_SERVER_NAME ); + if ( !NET_StringToAdr( UPDATE_SERVER_NAME, &cls.updateServer, NA_IP ) ) { + Com_Printf( "Couldn't resolve address\n" ); + return; + } + cls.updateServer.port = BigShort( PORT_UPDATE ); + Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", UPDATE_SERVER_NAME, + cls.updateServer.ip[0], cls.updateServer.ip[1], + cls.updateServer.ip[2], cls.updateServer.ip[3], + BigShort( cls.updateServer.port ) ); + + info[0] = 0; + + Com_sprintf( cls.updateChallenge, sizeof( cls.updateChallenge ), "%i", ((rand() << 16) ^ rand()) ^ Com_Milliseconds()); + + Info_SetValueForKey( info, "challenge", cls.updateChallenge ); + Info_SetValueForKey( info, "renderer", cls.glconfig.renderer_string ); + Info_SetValueForKey( info, "version", com_version->string ); + + NET_OutOfBandPrint( NS_CLIENT, cls.updateServer, "getmotd \"%s\"\n", info ); +} + +/* +=================== +CL_RequestAuthorization + +Authorization server protocol +----------------------------- + +All commands are text in Q3 out of band packets (leading 0xff 0xff 0xff 0xff). + +Whenever the client tries to get a challenge from the server it wants to +connect to, it also blindly fires off a packet to the authorize server: + +getKeyAuthorize + +cdkey may be "demo" + + +#OLD The authorize server returns a: +#OLD +#OLD keyAthorize +#OLD +#OLD A client will be accepted if the cdkey is valid and it has not been used by any other IP +#OLD address in the last 15 minutes. + + +The server sends a: + +getIpAuthorize + +The authorize server returns a: + +ipAuthorize + +A client will be accepted if a valid cdkey was sent by that ip (only) in the last 15 minutes. +If no response is received from the authorize server after two tries, the client will be let +in anyway. +=================== +*/ +#ifndef STANDALONE +void CL_RequestAuthorization( void ) { + char nums[64]; + int i, j, l; + cvar_t *fs; + + if ( !cls.authorizeServer.port ) { + Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); + if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &cls.authorizeServer, NA_IP ) ) { + Com_Printf( "Couldn't resolve address\n" ); + return; + } + + cls.authorizeServer.port = BigShort( PORT_AUTHORIZE ); + Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, + cls.authorizeServer.ip[0], cls.authorizeServer.ip[1], + cls.authorizeServer.ip[2], cls.authorizeServer.ip[3], + BigShort( cls.authorizeServer.port ) ); + } + if ( cls.authorizeServer.type == NA_BAD ) { + return; + } + + // only grab the alphanumeric values from the cdkey, to avoid any dashes or spaces + j = 0; + l = strlen( cl_cdkey ); + if ( l > 32 ) { + l = 32; + } + for ( i = 0 ; i < l ; i++ ) { + if ( ( cl_cdkey[i] >= '0' && cl_cdkey[i] <= '9' ) + || ( cl_cdkey[i] >= 'a' && cl_cdkey[i] <= 'z' ) + || ( cl_cdkey[i] >= 'A' && cl_cdkey[i] <= 'Z' ) + ) { + nums[j] = cl_cdkey[i]; + j++; + } + } + nums[j] = 0; + + fs = Cvar_Get ("cl_anonymous", "0", CVAR_INIT|CVAR_SYSTEMINFO ); + + NET_OutOfBandPrint(NS_CLIENT, cls.authorizeServer, "getKeyAuthorize %i %s", fs->integer, nums ); +} +#endif +/* +====================================================================== + +CONSOLE COMMANDS + +====================================================================== +*/ + +/* +================== +CL_ForwardToServer_f +================== +*/ +void CL_ForwardToServer_f( void ) { + if ( cls.state != CA_ACTIVE || clc.demoplaying ) { + Com_Printf ("Not connected to a server.\n"); + return; + } + + // don't forward the first argument + if ( Cmd_Argc() > 1 ) { + CL_AddReliableCommand( Cmd_Args() ); + } +} + +/* +================== +CL_Setenv_f + +Mostly for controlling voodoo environment variables +================== +*/ +void CL_Setenv_f( void ) { + int argc = Cmd_Argc(); + + if ( argc > 2 ) { + char buffer[1024]; + int i; + + strcpy( buffer, Cmd_Argv(1) ); + strcat( buffer, "=" ); + + for ( i = 2; i < argc; i++ ) { + strcat( buffer, Cmd_Argv( i ) ); + strcat( buffer, " " ); + } + + putenv( buffer ); + } else if ( argc == 2 ) { + char *env = getenv( Cmd_Argv(1) ); + + if ( env ) { + Com_Printf( "%s=%s\n", Cmd_Argv(1), env ); + } else { + Com_Printf( "%s undefined\n", Cmd_Argv(1)); + } + } +} + + +/* +================== +CL_Disconnect_f +================== +*/ +void CL_Disconnect_f( void ) { + SCR_StopCinematic(); + Cvar_Set("ui_singlePlayerActive", "0"); + if ( cls.state != CA_DISCONNECTED && cls.state != CA_CINEMATIC ) { + Com_Error (ERR_DISCONNECT, "Disconnected from server"); + } +} + + +/* +================ +CL_Reconnect_f + +================ +*/ +void CL_Reconnect_f( void ) { + if ( !strlen( cls.servername ) || !strcmp( cls.servername, "localhost" ) ) { + Com_Printf( "Can't reconnect to localhost.\n" ); + return; + } + Cvar_Set("ui_singlePlayerActive", "0"); + Cbuf_AddText( va("connect %s\n", cls.servername ) ); +} + +/* +================ +CL_Connect_f + +================ +*/ +void CL_Connect_f( void ) { + char *server; + const char *serverString; + int argc = Cmd_Argc(); + netadrtype_t family = NA_UNSPEC; + + if ( argc != 2 && argc != 3 ) { + Com_Printf( "usage: connect [-4|-6] server\n"); + return; + } + + if(argc == 2) + server = Cmd_Argv(1); + else + { + if(!strcmp(Cmd_Argv(1), "-4")) + family = NA_IP; + else if(!strcmp(Cmd_Argv(1), "-6")) + family = NA_IP6; + else + Com_Printf( "warning: only -4 or -6 as address type understood.\n"); + + server = Cmd_Argv(2); + } + + Cvar_Set("ui_singlePlayerActive", "0"); + + // fire a message off to the motd server + CL_RequestMotd(); + + // clear any previous "server full" type messages + clc.serverMessage[0] = 0; + + if ( com_sv_running->integer && !strcmp( server, "localhost" ) ) { + // if running a local server, kill it + SV_Shutdown( "Server quit" ); + } + + // make sure a local server is killed + Cvar_Set( "sv_killserver", "1" ); + SV_Frame( 0 ); + + CL_Disconnect( qtrue ); + Con_Close(); + + Q_strncpyz( cls.servername, server, sizeof(cls.servername) ); + + if (!NET_StringToAdr(cls.servername, &clc.serverAddress, family) ) { + Com_Printf ("Bad server address\n"); + cls.state = CA_DISCONNECTED; + return; + } + if (clc.serverAddress.port == 0) { + clc.serverAddress.port = BigShort( PORT_SERVER ); + } + + serverString = NET_AdrToStringwPort(clc.serverAddress); + + Com_Printf( "%s resolved to %s\n", cls.servername, serverString); + + if( cl_guidServerUniq->integer ) + CL_UpdateGUID( serverString, strlen( serverString ) ); + else + CL_UpdateGUID( NULL, 0 ); + + // if we aren't playing on a lan, we need to authenticate + // with the cd key + if(NET_IsLocalAddress(clc.serverAddress)) + cls.state = CA_CHALLENGING; + else + { + cls.state = CA_CONNECTING; + + // Set a client challenge number that ideally is mirrored back by the server. + clc.challenge = ((rand() << 16) ^ rand()) ^ Com_Milliseconds(); + } + + Key_SetCatcher( 0 ); + clc.connectTime = -99999; // CL_CheckForResend() will fire immediately + clc.connectPacketCount = 0; + + // server connection string + Cvar_Set( "cl_currentServerAddress", server ); +} + +#define MAX_RCON_MESSAGE 1024 + +/* +================== +CL_CompleteRcon +================== +*/ +static void CL_CompleteRcon( char *args, int argNum ) +{ + if( argNum == 2 ) + { + // Skip "rcon " + char *p = Com_SkipTokens( args, 1, " " ); + + if( p > args ) + Field_CompleteCommand( p, qtrue, qtrue ); + } +} + +/* +===================== +CL_Rcon_f + + Send the rest of the command line over as + an unconnected command. +===================== +*/ +void CL_Rcon_f( void ) { + char message[MAX_RCON_MESSAGE]; + netadr_t to; + + if ( !rcon_client_password->string ) { + Com_Printf ("You must set 'rconpassword' before\n" + "issuing an rcon command.\n"); + return; + } + + message[0] = -1; + message[1] = -1; + message[2] = -1; + message[3] = -1; + message[4] = 0; + + Q_strcat (message, MAX_RCON_MESSAGE, "rcon "); + + Q_strcat (message, MAX_RCON_MESSAGE, rcon_client_password->string); + Q_strcat (message, MAX_RCON_MESSAGE, " "); + + // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543 + Q_strcat (message, MAX_RCON_MESSAGE, Cmd_Cmd()+5); + + if ( cls.state >= CA_CONNECTED ) { + to = clc.netchan.remoteAddress; + } else { + if (!strlen(rconAddress->string)) { + Com_Printf ("You must either be connected,\n" + "or set the 'rconAddress' cvar\n" + "to issue rcon commands\n"); + + return; + } + NET_StringToAdr (rconAddress->string, &to, NA_UNSPEC); + if (to.port == 0) { + to.port = BigShort (PORT_SERVER); + } + } + + NET_SendPacket (NS_CLIENT, strlen(message)+1, message, to); +} + +/* +================= +CL_SendPureChecksums +================= +*/ +void CL_SendPureChecksums( void ) { + char cMsg[MAX_INFO_VALUE]; + + // if we are pure we need to send back a command with our referenced pk3 checksums + Com_sprintf(cMsg, sizeof(cMsg), "cp %d %s", cl.serverId, FS_ReferencedPakPureChecksums()); + + CL_AddReliableCommand( cMsg ); +} + +/* +================= +CL_ResetPureClientAtServer +================= +*/ +void CL_ResetPureClientAtServer( void ) { + CL_AddReliableCommand( va("vdr") ); +} + +/* +================= +CL_Vid_Restart_f + +Restart the video subsystem + +we also have to reload the UI and CGame because the renderer +doesn't know what graphics to reload +================= +*/ +void CL_Vid_Restart_f( void ) { + + // Settings may have changed so stop recording now + if( CL_VideoRecording( ) ) { + CL_CloseAVI( ); + } + + if(clc.demorecording) + CL_StopRecord_f(); + + // don't let them loop during the restart + S_StopAllSounds(); + // shutdown the UI + CL_ShutdownUI(); + // shutdown the CGame + CL_ShutdownCGame(); + // shutdown the renderer and clear the renderer interface + CL_ShutdownRef(); + // client is no longer pure untill new checksums are sent + CL_ResetPureClientAtServer(); + // clear pak references + FS_ClearPakReferences( FS_UI_REF | FS_CGAME_REF ); + // reinitialize the filesystem if the game directory or checksum has changed + FS_ConditionalRestart( clc.checksumFeed ); + + cls.rendererStarted = qfalse; + cls.uiStarted = qfalse; + cls.cgameStarted = qfalse; + cls.soundRegistered = qfalse; + + // unpause so the cgame definately gets a snapshot and renders a frame + Cvar_Set( "cl_paused", "0" ); + + // if not running a server clear the whole hunk + if ( !com_sv_running->integer ) { + // clear the whole hunk + Hunk_Clear(); + } + else { + // clear all the client data on the hunk + Hunk_ClearToMark(); + } + + // initialize the renderer interface + CL_InitRef(); + + // startup all the client stuff + CL_StartHunkUsers( qfalse ); + + // start the cgame if connected + if ( cls.state > CA_CONNECTED && cls.state != CA_CINEMATIC ) { + cls.cgameStarted = qtrue; + CL_InitCGame(); + // send pure checksums + CL_SendPureChecksums(); + } +} + +/* +================= +CL_Snd_Restart_f + +Restart the sound subsystem +The cgame and game must also be forced to restart because +handles will be invalid +================= +*/ +void CL_Snd_Restart_f( void ) { + S_Shutdown(); + S_Init(); + + CL_Vid_Restart_f(); +} + + +/* +================== +CL_PK3List_f +================== +*/ +void CL_OpenedPK3List_f( void ) { + Com_Printf("Opened PK3 Names: %s\n", FS_LoadedPakNames()); +} + +/* +================== +CL_PureList_f +================== +*/ +void CL_ReferencedPK3List_f( void ) { + Com_Printf("Referenced PK3 Names: %s\n", FS_ReferencedPakNames()); +} + +/* +================== +CL_Configstrings_f +================== +*/ +void CL_Configstrings_f( void ) { + int i; + int ofs; + + if ( cls.state != CA_ACTIVE ) { + Com_Printf( "Not connected to a server.\n"); + return; + } + + for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { + ofs = cl.gameState.stringOffsets[ i ]; + if ( !ofs ) { + continue; + } + Com_Printf( "%4i: %s\n", i, cl.gameState.stringData + ofs ); + } +} + +/* +============== +CL_Clientinfo_f +============== +*/ +void CL_Clientinfo_f( void ) { + Com_Printf( "--------- Client Information ---------\n" ); + Com_Printf( "state: %i\n", cls.state ); + Com_Printf( "Server: %s\n", cls.servername ); + Com_Printf ("User info settings:\n"); + Info_Print( Cvar_InfoString( CVAR_USERINFO ) ); + Com_Printf( "--------------------------------------\n" ); +} + + +//==================================================================== + +/* +================= +CL_DownloadsComplete + +Called when all downloading has been completed +================= +*/ +void CL_DownloadsComplete( void ) { + +#ifdef USE_CURL + // if we downloaded with cURL + if(clc.cURLUsed) { + clc.cURLUsed = qfalse; + CL_cURL_Shutdown(); + if( clc.cURLDisconnected ) { + if(clc.downloadRestart) { + FS_Restart(clc.checksumFeed); + clc.downloadRestart = qfalse; + } + clc.cURLDisconnected = qfalse; + CL_Reconnect_f(); + return; + } + } +#endif + + // if we downloaded files we need to restart the file system + if (clc.downloadRestart) { + clc.downloadRestart = qfalse; + + FS_Restart(clc.checksumFeed); // We possibly downloaded a pak, restart the file system to load it + + // inform the server so we get new gamestate info + CL_AddReliableCommand( "donedl" ); + + // by sending the donedl command we request a new gamestate + // so we don't want to load stuff yet + return; + } + + // let the client game init and load data + cls.state = CA_LOADING; + + // Pump the loop, this may change gamestate! + Com_EventLoop(); + + // if the gamestate was changed by calling Com_EventLoop + // then we loaded everything already and we don't want to do it again. + if ( cls.state != CA_LOADING ) { + return; + } + + // starting to load a map so we get out of full screen ui mode + Cvar_Set("r_uiFullScreen", "0"); + + // flush client memory and start loading stuff + // this will also (re)load the UI + // if this is a local client then only the client part of the hunk + // will be cleared, note that this is done after the hunk mark has been set + CL_FlushMemory(); + + // initialize the CGame + cls.cgameStarted = qtrue; + CL_InitCGame(); + + // set pure checksums + CL_SendPureChecksums(); + + CL_WritePacket(); + CL_WritePacket(); + CL_WritePacket(); +} + +/* +================= +CL_BeginDownload + +Requests a file to download from the server. Stores it in the current +game directory. +================= +*/ +void CL_BeginDownload( const char *localName, const char *remoteName ) { + + Com_DPrintf("***** CL_BeginDownload *****\n" + "Localname: %s\n" + "Remotename: %s\n" + "****************************\n", localName, remoteName); + + Q_strncpyz ( clc.downloadName, localName, sizeof(clc.downloadName) ); + Com_sprintf( clc.downloadTempName, sizeof(clc.downloadTempName), "%s.tmp", localName ); + + // Set so UI gets access to it + Cvar_Set( "cl_downloadName", remoteName ); + Cvar_Set( "cl_downloadSize", "0" ); + Cvar_Set( "cl_downloadCount", "0" ); + Cvar_SetValue( "cl_downloadTime", cls.realtime ); + + clc.downloadBlock = 0; // Starting new file + clc.downloadCount = 0; + + CL_AddReliableCommand( va("download %s", remoteName) ); +} + +/* +================= +CL_NextDownload + +A download completed or failed +================= +*/ +void CL_NextDownload(void) { + char *s; + char *remoteName, *localName; + qboolean useCURL = qfalse; + + // We are looking to start a download here + if (*clc.downloadList) { + s = clc.downloadList; + + // format is: + // @remotename@localname@remotename@localname, etc. + + if (*s == '@') + s++; + remoteName = s; + + if ( (s = strchr(s, '@')) == NULL ) { + CL_DownloadsComplete(); + return; + } + + *s++ = 0; + localName = s; + if ( (s = strchr(s, '@')) != NULL ) + *s++ = 0; + else + s = localName + strlen(localName); // point at the nul byte +#ifdef USE_CURL + if(!(cl_allowDownload->integer & DLF_NO_REDIRECT)) { + if(clc.sv_allowDownload & DLF_NO_REDIRECT) { + Com_Printf("WARNING: server does not " + "allow download redirection " + "(sv_allowDownload is %d)\n", + clc.sv_allowDownload); + } + else if(!*clc.sv_dlURL) { + Com_Printf("WARNING: server allows " + "download redirection, but does not " + "have sv_dlURL set\n"); + } + else if(!CL_cURL_Init()) { + Com_Printf("WARNING: could not load " + "cURL library\n"); + } + else { + CL_cURL_BeginDownload(localName, va("%s/%s", + clc.sv_dlURL, remoteName)); + useCURL = qtrue; + } + } + else if(!(clc.sv_allowDownload & DLF_NO_REDIRECT)) { + Com_Printf("WARNING: server allows download " + "redirection, but it disabled by client " + "configuration (cl_allowDownload is %d)\n", + cl_allowDownload->integer); + } +#endif /* USE_CURL */ + if(!useCURL) { + if((cl_allowDownload->integer & DLF_NO_UDP)) { + Com_Error(ERR_DROP, "UDP Downloads are " + "disabled on your client. " + "(cl_allowDownload is %d)", + cl_allowDownload->integer); + return; + } + else { + CL_BeginDownload( localName, remoteName ); + } + } + clc.downloadRestart = qtrue; + + // move over the rest + memmove( clc.downloadList, s, strlen(s) + 1); + + return; + } + + CL_DownloadsComplete(); +} + +/* +================= +CL_InitDownloads + +After receiving a valid game state, we valid the cgame and local zip files here +and determine if we need to download them +================= +*/ +void CL_InitDownloads(void) { + char missingfiles[1024]; + + if ( !(cl_allowDownload->integer & DLF_ENABLE) ) + { + // autodownload is disabled on the client + // but it's possible that some referenced files on the server are missing + if (FS_ComparePaks( missingfiles, sizeof( missingfiles ), qfalse ) ) + { + // NOTE TTimo I would rather have that printed as a modal message box + // but at this point while joining the game we don't know wether we will successfully join or not + Com_Printf( "\nWARNING: You are missing some files referenced by the server:\n%s" + "You might not be able to join the game\n" + "Go to the setting menu to turn on autodownload, or get the file elsewhere\n\n", missingfiles ); + } + } + else if ( FS_ComparePaks( clc.downloadList, sizeof( clc.downloadList ) , qtrue ) ) { + + Com_Printf("Need paks: %s\n", clc.downloadList ); + + if ( *clc.downloadList ) { + // if autodownloading is not enabled on the server + cls.state = CA_CONNECTED; + CL_NextDownload(); + return; + } + + } + + CL_DownloadsComplete(); +} + +/* +================= +CL_CheckForResend + +Resend a connect message if the last one has timed out +================= +*/ +void CL_CheckForResend( void ) { + int port, i; + char info[MAX_INFO_STRING]; + char data[MAX_INFO_STRING]; + + // don't send anything if playing back a demo + if ( clc.demoplaying ) { + return; + } + + // resend if we haven't gotten a reply yet + if ( cls.state != CA_CONNECTING && cls.state != CA_CHALLENGING ) { + return; + } + + if ( cls.realtime - clc.connectTime < RETRANSMIT_TIMEOUT ) { + return; + } + + clc.connectTime = cls.realtime; // for retransmit requests + clc.connectPacketCount++; + + + switch ( cls.state ) { + case CA_CONNECTING: + // requesting a challenge .. IPv6 users always get in as authorize server supports no ipv6. +#ifndef STANDALONE + if (!Cvar_VariableIntegerValue("com_standalone") && clc.serverAddress.type == NA_IP && !Sys_IsLANAddress( clc.serverAddress ) ) + CL_RequestAuthorization(); +#endif + + // The challenge request shall be followed by a client challenge so no malicious server can hijack this connection. + Com_sprintf(data, sizeof(data), "getchallenge %d", clc.challenge); + + NET_OutOfBandPrint(NS_CLIENT, clc.serverAddress, data); + break; + + case CA_CHALLENGING: + // sending back the challenge + port = Cvar_VariableValue ("net_qport"); + + Q_strncpyz( info, Cvar_InfoString( CVAR_USERINFO ), sizeof( info ) ); + Info_SetValueForKey( info, "protocol", va("%i", PROTOCOL_VERSION ) ); + Info_SetValueForKey( info, "qport", va("%i", port ) ); + Info_SetValueForKey( info, "challenge", va("%i", clc.challenge ) ); + + strcpy(data, "connect "); + // TTimo adding " " around the userinfo string to avoid truncated userinfo on the server + // (Com_TokenizeString tokenizes around spaces) + data[8] = '"'; + + for(i=0;iadr = *address; + server->clients = 0; + server->hostName[0] = '\0'; + server->mapName[0] = '\0'; + server->maxClients = 0; + server->maxPing = 0; + server->minPing = 0; + server->ping = -1; + server->game[0] = '\0'; + server->gameType = 0; + server->netType = 0; +} + +#define MAX_SERVERSPERPACKET 256 + +/* +=================== +CL_ServersResponsePacket +=================== +*/ +void CL_ServersResponsePacket( const netadr_t* from, msg_t *msg, qboolean extended ) { + int i, count, total; + netadr_t addresses[MAX_SERVERSPERPACKET]; + int numservers; + byte* buffptr; + byte* buffend; + + Com_Printf("CL_ServersResponsePacket\n"); + + if (cls.numglobalservers == -1) { + // state to detect lack of servers or lack of response + cls.numglobalservers = 0; + cls.numGlobalServerAddresses = 0; + } + + // parse through server response string + numservers = 0; + buffptr = msg->data; + buffend = buffptr + msg->cursize; + + // advance to initial token + do + { + if(*buffptr == '\\' || (extended && *buffptr == '/')) + break; + + buffptr++; + } while (buffptr < buffend); + + while (buffptr + 1 < buffend) + { + // IPv4 address + if (*buffptr == '\\') + { + buffptr++; + + if (buffend - buffptr < sizeof(addresses[numservers].ip) + sizeof(addresses[numservers].port) + 1) + break; + + for(i = 0; i < sizeof(addresses[numservers].ip); i++) + addresses[numservers].ip[i] = *buffptr++; + + addresses[numservers].type = NA_IP; + } + // IPv6 address, if it's an extended response + else if (extended && *buffptr == '/') + { + buffptr++; + + if (buffend - buffptr < sizeof(addresses[numservers].ip6) + sizeof(addresses[numservers].port) + 1) + break; + + for(i = 0; i < sizeof(addresses[numservers].ip6); i++) + addresses[numservers].ip6[i] = *buffptr++; + + addresses[numservers].type = NA_IP6; + addresses[numservers].scope_id = from->scope_id; + } + else + // syntax error! + break; + + // parse out port + addresses[numservers].port = (*buffptr++) << 8; + addresses[numservers].port += *buffptr++; + addresses[numservers].port = BigShort( addresses[numservers].port ); + + // syntax check + if (*buffptr != '\\' && *buffptr != '/') + break; + + numservers++; + if (numservers >= MAX_SERVERSPERPACKET) + break; + } + + count = cls.numglobalservers; + + for (i = 0; i < numservers && count < MAX_GLOBAL_SERVERS; i++) { + // build net address + serverInfo_t *server = &cls.globalServers[count]; + + CL_InitServerInfo( server, &addresses[i] ); + // advance to next slot + count++; + } + + // if getting the global list + if ( count >= MAX_GLOBAL_SERVERS && cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS ) + { + // if we couldn't store the servers in the main list anymore + for (; i < numservers && cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS; i++) + { + // just store the addresses in an additional list + cls.globalServerAddresses[cls.numGlobalServerAddresses++] = addresses[i]; + } + } + + cls.numglobalservers = count; + total = count + cls.numGlobalServerAddresses; + + Com_Printf("%d servers parsed (total %d)\n", numservers, total); +} + +/* +================= +CL_ConnectionlessPacket + +Responses to broadcasts, etc +================= +*/ +void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { + char *s; + char *c; + + MSG_BeginReadingOOB( msg ); + MSG_ReadLong( msg ); // skip the -1 + + s = MSG_ReadStringLine( msg ); + + Cmd_TokenizeString( s ); + + c = Cmd_Argv(0); + + Com_DPrintf ("CL packet %s: %s\n", NET_AdrToStringwPort(from), c); + + // challenge from the server we are connecting to + if (!Q_stricmp(c, "challengeResponse")) + { + if (cls.state != CA_CONNECTING) + { + Com_DPrintf("Unwanted challenge response received. Ignored.\n"); + return; + } + + if(!NET_CompareAdr(from, clc.serverAddress)) + { + // This challenge response is not coming from the expected address. + // Check whether we have a matching client challenge to prevent + // connection hi-jacking. + + c = Cmd_Argv(2); + + if(!*c || atoi(c) != clc.challenge) + { + Com_DPrintf("Challenge response received from unexpected source. Ignored.\n"); + return; + } + } + + // start sending challenge response instead of challenge request packets + clc.challenge = atoi(Cmd_Argv(1)); + cls.state = CA_CHALLENGING; + clc.connectPacketCount = 0; + clc.connectTime = -99999; + + // take this address as the new server address. This allows + // a server proxy to hand off connections to multiple servers + clc.serverAddress = from; + Com_DPrintf ("challengeResponse: %d\n", clc.challenge); + return; + } + + // server connection + if ( !Q_stricmp(c, "connectResponse") ) { + if ( cls.state >= CA_CONNECTED ) { + Com_Printf ("Dup connect received. Ignored.\n"); + return; + } + if ( cls.state != CA_CHALLENGING ) { + Com_Printf ("connectResponse packet while not connecting. Ignored.\n"); + return; + } + if ( !NET_CompareAdr( from, clc.serverAddress ) ) { + Com_Printf( "connectResponse from wrong address. Ignored.\n" ); + return; + } + Netchan_Setup (NS_CLIENT, &clc.netchan, from, Cvar_VariableValue( "net_qport" ) ); + cls.state = CA_CONNECTED; + clc.lastPacketSentTime = -9999; // send first packet immediately + return; + } + + // server responding to an info broadcast + if ( !Q_stricmp(c, "infoResponse") ) { + CL_ServerInfoPacket( from, msg ); + return; + } + + // server responding to a get playerlist + if ( !Q_stricmp(c, "statusResponse") ) { + CL_ServerStatusResponse( from, msg ); + return; + } + + // a disconnect message from the server, which will happen if the server + // dropped the connection but it is still getting packets from us + if (!Q_stricmp(c, "disconnect")) { + CL_DisconnectPacket( from ); + return; + } + + // echo request from server + if ( !Q_stricmp(c, "echo") ) { + NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv(1) ); + return; + } + + // cd check + if ( !Q_stricmp(c, "keyAuthorize") ) { + // we don't use these now, so dump them on the floor + return; + } + + // global MOTD from id + if ( !Q_stricmp(c, "motd") ) { + CL_MotdPacket( from ); + return; + } + + // echo request from server + if ( !Q_stricmp(c, "print") ) { + s = MSG_ReadString( msg ); + Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) ); + Com_Printf( "%s", s ); + return; + } + + // list of servers sent back by a master server (classic) + if ( !Q_strncmp(c, "getserversResponse", 18) ) { + CL_ServersResponsePacket( &from, msg, qfalse ); + return; + } + + // list of servers sent back by a master server (extended) + if ( !Q_strncmp(c, "getserversExtResponse", 21) ) { + CL_ServersResponsePacket( &from, msg, qtrue ); + return; + } + + Com_DPrintf ("Unknown connectionless packet command.\n"); +} + + +/* +================= +CL_PacketEvent + +A packet has arrived from the main event loop +================= +*/ +void CL_PacketEvent( netadr_t from, msg_t *msg ) { + int headerBytes; + + clc.lastPacketTime = cls.realtime; + + if ( msg->cursize >= 4 && *(int *)msg->data == -1 ) { + CL_ConnectionlessPacket( from, msg ); + return; + } + + if ( cls.state < CA_CONNECTED ) { + return; // can't be a valid sequenced packet + } + + if ( msg->cursize < 4 ) { + Com_Printf ("%s: Runt packet\n", NET_AdrToStringwPort( from )); + return; + } + + // + // packet from server + // + if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) { + Com_DPrintf ("%s:sequenced packet without connection\n" + , NET_AdrToStringwPort( from ) ); + // FIXME: send a client disconnect? + return; + } + + if (!CL_Netchan_Process( &clc.netchan, msg) ) { + return; // out of order, duplicated, etc + } + + // the header is different lengths for reliable and unreliable messages + headerBytes = msg->readcount; + + // track the last message received so it can be returned in + // client messages, allowing the server to detect a dropped + // gamestate + clc.serverMessageSequence = LittleLong( *(int *)msg->data ); + + clc.lastPacketTime = cls.realtime; + CL_ParseServerMessage( msg ); + + // + // we don't know if it is ok to save a demo message until + // after we have parsed the frame + // + if ( clc.demorecording && !clc.demowaiting ) { + CL_WriteDemoMessage( msg, headerBytes ); + } +} + +/* +================== +CL_CheckTimeout + +================== +*/ +void CL_CheckTimeout( void ) { + // + // check timeout + // + if ( ( !CL_CheckPaused() || !sv_paused->integer ) + && cls.state >= CA_CONNECTED && cls.state != CA_CINEMATIC + && cls.realtime - clc.lastPacketTime > cl_timeout->value*1000) { + if (++cl.timeoutcount > 5) { // timeoutcount saves debugger + Com_Printf ("\nServer connection timed out.\n"); + CL_Disconnect( qtrue ); + return; + } + } else { + cl.timeoutcount = 0; + } +} + +/* +================== +CL_CheckPaused +Check whether client has been paused. +================== +*/ +qboolean CL_CheckPaused(void) +{ + // if cl_paused->modified is set, the cvar has only been changed in + // this frame. Keep paused in this frame to ensure the server doesn't + // lag behind. + if(cl_paused->integer || cl_paused->modified) + return qtrue; + + return qfalse; +} + +//============================================================================ + +/* +================== +CL_CheckUserinfo + +================== +*/ +void CL_CheckUserinfo( void ) { + // don't add reliable commands when not yet connected + if(cls.state < CA_CHALLENGING) + return; + + // don't overflow the reliable command buffer when paused + if(CL_CheckPaused()) + return; + + // send a reliable userinfo update if needed + if(cvar_modifiedFlags & CVAR_USERINFO) + { + cvar_modifiedFlags &= ~CVAR_USERINFO; + CL_AddReliableCommand( va("userinfo \"%s\"", Cvar_InfoString( CVAR_USERINFO ) ) ); + } +} + +/* +================== +CL_Frame + +================== +*/ +void CL_Frame ( int msec ) { + + if ( !com_cl_running->integer ) { + return; + } + +#ifdef USE_CURL + if(clc.downloadCURLM) { + CL_cURL_PerformDownload(); + // we can't process frames normally when in disconnected + // download mode since the ui vm expects cls.state to be + // CA_CONNECTED + if(clc.cURLDisconnected) { + cls.realFrametime = msec; + cls.frametime = msec; + cls.realtime += cls.frametime; + SCR_UpdateScreen(); + S_Update(); + Con_RunConsole(); + cls.framecount++; + return; + } + } +#endif + + if ( cls.cddialog ) { + // bring up the cd error dialog if needed + cls.cddialog = qfalse; + VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NEED_CD ); + } else if ( cls.state == CA_DISCONNECTED && !( Key_GetCatcher( ) & KEYCATCH_UI ) + && !com_sv_running->integer && uivm ) { + // if disconnected, bring up the menu + S_StopAllSounds(); + VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN ); + } + + // if recording an avi, lock to a fixed fps + if ( CL_VideoRecording( ) && cl_aviFrameRate->integer && msec) { + // save the current screen + if ( cls.state == CA_ACTIVE || cl_forceavidemo->integer) { + CL_TakeVideoFrame( ); + + // fixed time for next frame' + msec = (int)ceil( (1000.0f / cl_aviFrameRate->value) * com_timescale->value ); + if (msec == 0) { + msec = 1; + } + } + } + + if( cl_autoRecordDemo->integer ) { + if( cls.state == CA_ACTIVE && !clc.demorecording && !clc.demoplaying ) { + // If not recording a demo, and we should be, start one + qtime_t now; + char *nowString; + char *p; + char mapName[ MAX_QPATH ]; + char serverName[ MAX_OSPATH ]; + + Com_RealTime( &now ); + nowString = va( "%04d%02d%02d%02d%02d%02d", + 1900 + now.tm_year, + 1 + now.tm_mon, + now.tm_mday, + now.tm_hour, + now.tm_min, + now.tm_sec ); + + Q_strncpyz( serverName, cls.servername, MAX_OSPATH ); + // Replace the ":" in the address as it is not a valid + // file name character + p = strstr( serverName, ":" ); + if( p ) { + *p = '.'; + } + + Q_strncpyz( mapName, COM_SkipPath( cl.mapname ), sizeof( cl.mapname ) ); + COM_StripExtension(mapName, mapName, sizeof(mapName)); + + Cbuf_ExecuteText( EXEC_NOW, + va( "record %s-%s-%s", nowString, serverName, mapName ) ); + } + else if( cls.state != CA_ACTIVE && clc.demorecording ) { + // Recording, but not CA_ACTIVE, so stop recording + CL_StopRecord_f( ); + } + } + + // save the msec before checking pause + cls.realFrametime = msec; + + // decide the simulation time + cls.frametime = msec; + + cls.realtime += cls.frametime; + + if ( cl_timegraph->integer ) { + SCR_DebugGraph ( cls.realFrametime * 0.25, 0 ); + } + + // see if we need to update any userinfo + CL_CheckUserinfo(); + + // if we haven't gotten a packet in a long time, + // drop the connection + CL_CheckTimeout(); + + // send intentions now + CL_SendCmd(); + + // resend a connection request if necessary + CL_CheckForResend(); + + // decide on the serverTime to render + CL_SetCGameTime(); + + // update the screen + SCR_UpdateScreen(); + + // update audio + S_Update(); + +#ifdef USE_VOIP + CL_CaptureVoip(); +#endif + +#ifdef USE_MUMBLE + CL_UpdateMumble(); +#endif + + // advance local effects for next frame + SCR_RunCinematic(); + + Con_RunConsole(); + + cls.framecount++; +} + + +//============================================================================ + +/* +================ +CL_RefPrintf + +DLL glue +================ +*/ +void QDECL CL_RefPrintf( int print_level, const char *fmt, ...) { + va_list argptr; + char msg[MAXPRINTMSG]; + + va_start (argptr,fmt); + Q_vsnprintf (msg, sizeof(msg), fmt, argptr); + va_end (argptr); + + if ( print_level == PRINT_ALL ) { + Com_Printf ("%s", msg); + } else if ( print_level == PRINT_WARNING ) { + Com_Printf (S_COLOR_YELLOW "%s", msg); // yellow + } else if ( print_level == PRINT_DEVELOPER ) { + Com_DPrintf (S_COLOR_RED "%s", msg); // red + } +} + + + +/* +============ +CL_ShutdownRef +============ +*/ +void CL_ShutdownRef( void ) { + if ( !re.Shutdown ) { + return; + } + re.Shutdown( qtrue ); + Com_Memset( &re, 0, sizeof( re ) ); +} + +/* +============ +CL_InitRenderer +============ +*/ +void CL_InitRenderer( void ) { + // this sets up the renderer and calls R_Init + re.BeginRegistration( &cls.glconfig ); + + // load character sets + cls.charSetShader = re.RegisterShader( "gfx/2d/bigchars" ); + cls.whiteShader = re.RegisterShader( "white" ); + cls.consoleShader = re.RegisterShader( "console" ); + g_console_field_width = cls.glconfig.vidWidth / SMALLCHAR_WIDTH - 2; + g_consoleField.widthInChars = g_console_field_width; +} + +/* +============================ +CL_StartHunkUsers + +After the server has cleared the hunk, these will need to be restarted +This is the only place that any of these functions are called from +============================ +*/ +void CL_StartHunkUsers( qboolean rendererOnly ) { + if (!com_cl_running) { + return; + } + + if ( !com_cl_running->integer ) { + return; + } + + if ( !cls.rendererStarted ) { + cls.rendererStarted = qtrue; + CL_InitRenderer(); + } + + if ( rendererOnly ) { + return; + } + + if ( !cls.soundStarted ) { + cls.soundStarted = qtrue; + S_Init(); + } + + if ( !cls.soundRegistered ) { + cls.soundRegistered = qtrue; + S_BeginRegistration(); + } + + if( com_dedicated->integer ) { + return; + } + + if ( !cls.uiStarted ) { + cls.uiStarted = qtrue; + CL_InitUI(); + } +} + +/* +============ +CL_RefMalloc +============ +*/ +void *CL_RefMalloc( int size ) { + return Z_TagMalloc( size, TAG_RENDERER ); +} + +int CL_ScaledMilliseconds(void) { + return Sys_Milliseconds()*com_timescale->value; +} + +/* +============ +CL_InitRef +============ +*/ +void CL_InitRef( void ) { + refimport_t ri; + refexport_t *ret; + + Com_Printf( "----- Initializing Renderer ----\n" ); + + ri.Cmd_AddCommand = Cmd_AddCommand; + ri.Cmd_RemoveCommand = Cmd_RemoveCommand; + ri.Cmd_Argc = Cmd_Argc; + ri.Cmd_Argv = Cmd_Argv; + ri.Cmd_ExecuteText = Cbuf_ExecuteText; + ri.Printf = CL_RefPrintf; + ri.Error = Com_Error; + ri.Milliseconds = CL_ScaledMilliseconds; + ri.Malloc = CL_RefMalloc; + ri.Free = Z_Free; +#ifdef HUNK_DEBUG + ri.Hunk_AllocDebug = Hunk_AllocDebug; +#else + ri.Hunk_Alloc = Hunk_Alloc; +#endif + ri.Hunk_AllocateTempMemory = Hunk_AllocateTempMemory; + ri.Hunk_FreeTempMemory = Hunk_FreeTempMemory; + ri.CM_DrawDebugSurface = CM_DrawDebugSurface; + ri.FS_ReadFile = FS_ReadFile; + ri.FS_FreeFile = FS_FreeFile; + ri.FS_WriteFile = FS_WriteFile; + ri.FS_FreeFileList = FS_FreeFileList; + ri.FS_ListFiles = FS_ListFiles; + ri.FS_FileIsInPAK = FS_FileIsInPAK; + ri.FS_FileExists = FS_FileExists; + ri.Cvar_Get = Cvar_Get; + ri.Cvar_Set = Cvar_Set; + ri.Cvar_CheckRange = Cvar_CheckRange; + + // cinematic stuff + + ri.CIN_UploadCinematic = CIN_UploadCinematic; + ri.CIN_PlayCinematic = CIN_PlayCinematic; + ri.CIN_RunCinematic = CIN_RunCinematic; + + ri.CL_WriteAVIVideoFrame = CL_WriteAVIVideoFrame; + + ret = GetRefAPI( REF_API_VERSION, &ri ); + +#if defined __USEA3D && defined __A3D_GEOM + hA3Dg_ExportRenderGeom (ret); +#endif + + Com_Printf( "-------------------------------\n"); + + if ( !ret ) { + Com_Error (ERR_FATAL, "Couldn't initialize refresh" ); + } + + re = *ret; + + // unpause so the cgame definately gets a snapshot and renders a frame + Cvar_Set( "cl_paused", "0" ); +} + + +//=========================================================================================== + + +void CL_SetModel_f( void ) { + char *arg; + char name[256]; + + arg = Cmd_Argv( 1 ); + if (arg[0]) { + Cvar_Set( "model", arg ); + Cvar_Set( "headmodel", arg ); + } else { + Cvar_VariableStringBuffer( "model", name, sizeof(name) ); + Com_Printf("model is set to %s\n", name); + } +} + + +//=========================================================================================== + + +/* +=============== +CL_Video_f + +video +video [filename] +=============== +*/ +void CL_Video_f( void ) +{ + char filename[ MAX_OSPATH ]; + int i, last; + + if( !clc.demoplaying ) + { + Com_Printf( "The video command can only be used when playing back demos\n" ); + return; + } + + if( Cmd_Argc( ) == 2 ) + { + // explicit filename + Com_sprintf( filename, MAX_OSPATH, "videos/%s.avi", Cmd_Argv( 1 ) ); + } + else + { + // scan for a free filename + for( i = 0; i <= 9999; i++ ) + { + int a, b, c, d; + + last = i; + + a = last / 1000; + last -= a * 1000; + b = last / 100; + last -= b * 100; + c = last / 10; + last -= c * 10; + d = last; + + Com_sprintf( filename, MAX_OSPATH, "videos/video%d%d%d%d.avi", + a, b, c, d ); + + if( !FS_FileExists( filename ) ) + break; // file doesn't exist + } + + if( i > 9999 ) + { + Com_Printf( S_COLOR_RED "ERROR: no free file names to create video\n" ); + return; + } + } + + CL_OpenAVIForWriting( filename ); +} + +/* +=============== +CL_StopVideo_f +=============== +*/ +void CL_StopVideo_f( void ) +{ + CL_CloseAVI( ); +} + +/* +=============== +CL_GenerateQKey + +test to see if a valid QKEY_FILE exists. If one does not, try to generate +it by filling it with 2048 bytes of random data. +=============== +*/ +static void CL_GenerateQKey(void) +{ + int len = 0; + unsigned char buff[ QKEY_SIZE ]; + fileHandle_t f; + + len = FS_SV_FOpenFileRead( QKEY_FILE, &f ); + FS_FCloseFile( f ); + if( len == QKEY_SIZE ) { + Com_Printf( "QKEY found.\n" ); + return; + } + else { + if( len > 0 ) { + Com_Printf( "QKEY file size != %d, regenerating\n", + QKEY_SIZE ); + } + + Com_Printf( "QKEY building random string\n" ); + Com_RandomBytes( buff, sizeof(buff) ); + + f = FS_SV_FOpenFileWrite( QKEY_FILE ); + if( !f ) { + Com_Printf( "QKEY could not open %s for write\n", + QKEY_FILE ); + return; + } + FS_Write( buff, sizeof(buff), f ); + FS_FCloseFile( f ); + Com_Printf( "QKEY generated\n" ); + } +} + +/* +==================== +CL_Init +==================== +*/ +void CL_Init( void ) { + Com_Printf( "----- Client Initialization -----\n" ); + + Con_Init (); + + CL_ClearState (); + + cls.state = CA_DISCONNECTED; // no longer CA_UNINITIALIZED + + cls.realtime = 0; + + CL_InitInput (); + + // + // register our variables + // + cl_noprint = Cvar_Get( "cl_noprint", "0", 0 ); + cl_motd = Cvar_Get ("cl_motd", "1", 0); + + cl_timeout = Cvar_Get ("cl_timeout", "200", 0); + + cl_timeNudge = Cvar_Get ("cl_timeNudge", "0", CVAR_TEMP ); + cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_TEMP ); + cl_showSend = Cvar_Get ("cl_showSend", "0", CVAR_TEMP ); + cl_showTimeDelta = Cvar_Get ("cl_showTimeDelta", "0", CVAR_TEMP ); + cl_freezeDemo = Cvar_Get ("cl_freezeDemo", "0", CVAR_TEMP ); + rcon_client_password = Cvar_Get ("rconPassword", "", CVAR_TEMP ); + cl_activeAction = Cvar_Get( "activeAction", "", CVAR_TEMP ); + + cl_timedemo = Cvar_Get ("timedemo", "0", 0); + cl_timedemoLog = Cvar_Get ("cl_timedemoLog", "", CVAR_ARCHIVE); + cl_autoRecordDemo = Cvar_Get ("cl_autoRecordDemo", "0", CVAR_ARCHIVE); + cl_aviFrameRate = Cvar_Get ("cl_aviFrameRate", "25", CVAR_ARCHIVE); + cl_aviMotionJpeg = Cvar_Get ("cl_aviMotionJpeg", "1", CVAR_ARCHIVE); + cl_forceavidemo = Cvar_Get ("cl_forceavidemo", "0", 0); + + rconAddress = Cvar_Get ("rconAddress", "", 0); + + cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", CVAR_ARCHIVE); + cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "140", CVAR_ARCHIVE); + cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", 0); + + cl_maxpackets = Cvar_Get ("cl_maxpackets", "30", CVAR_ARCHIVE ); + cl_packetdup = Cvar_Get ("cl_packetdup", "1", CVAR_ARCHIVE ); + + cl_run = Cvar_Get ("cl_run", "1", CVAR_ARCHIVE); + cl_sensitivity = Cvar_Get ("sensitivity", "5", CVAR_ARCHIVE); + cl_mouseAccel = Cvar_Get ("cl_mouseAccel", "0", CVAR_ARCHIVE); + cl_freelook = Cvar_Get( "cl_freelook", "1", CVAR_ARCHIVE ); + + cl_showMouseRate = Cvar_Get ("cl_showmouserate", "0", 0); + + cl_allowDownload = Cvar_Get ("cl_allowDownload", "0", CVAR_ARCHIVE); +#ifdef USE_CURL + cl_cURLLib = Cvar_Get("cl_cURLLib", DEFAULT_CURL_LIB, CVAR_ARCHIVE); +#endif + + cl_conXOffset = Cvar_Get ("cl_conXOffset", "0", 0); +#ifdef MACOS_X + // In game video is REALLY slow in Mac OS X right now due to driver slowness + cl_inGameVideo = Cvar_Get ("r_inGameVideo", "0", CVAR_ARCHIVE); +#else + cl_inGameVideo = Cvar_Get ("r_inGameVideo", "1", CVAR_ARCHIVE); +#endif + + cl_serverStatusResendTime = Cvar_Get ("cl_serverStatusResendTime", "750", 0); + + // init autoswitch so the ui will have it correctly even + // if the cgame hasn't been started + Cvar_Get ("cg_autoswitch", "1", CVAR_ARCHIVE); + + m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE); + m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_ARCHIVE); + m_forward = Cvar_Get ("m_forward", "0.25", CVAR_ARCHIVE); + m_side = Cvar_Get ("m_side", "0.25", CVAR_ARCHIVE); +#ifdef MACOS_X + // Input is jittery on OS X w/o this + m_filter = Cvar_Get ("m_filter", "1", CVAR_ARCHIVE); +#else + m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE); +#endif + + cl_motdString = Cvar_Get( "cl_motdString", "", CVAR_ROM ); + + Cvar_Get( "cl_maxPing", "800", CVAR_ARCHIVE ); + + cl_lanForcePackets = Cvar_Get ("cl_lanForcePackets", "1", CVAR_ARCHIVE); + + cl_guidServerUniq = Cvar_Get ("cl_guidServerUniq", "1", CVAR_ARCHIVE); + + // ~ and `, as keys and characters + cl_consoleKeys = Cvar_Get( "cl_consoleKeys", "~ ` 0x7e 0x60", CVAR_ARCHIVE); + + // userinfo + Cvar_Get ("name", "UnnamedPlayer", CVAR_USERINFO | CVAR_ARCHIVE ); + Cvar_Get ("rate", "3000", CVAR_USERINFO | CVAR_ARCHIVE ); + Cvar_Get ("snaps", "20", CVAR_USERINFO | CVAR_ARCHIVE ); + Cvar_Get ("model", "sarge", CVAR_USERINFO | CVAR_ARCHIVE ); + Cvar_Get ("headmodel", "sarge", CVAR_USERINFO | CVAR_ARCHIVE ); + Cvar_Get ("team_model", "james", CVAR_USERINFO | CVAR_ARCHIVE ); + Cvar_Get ("team_headmodel", "*james", CVAR_USERINFO | CVAR_ARCHIVE ); + Cvar_Get ("g_redTeam", "Stroggs", CVAR_SERVERINFO | CVAR_ARCHIVE); + Cvar_Get ("g_blueTeam", "Pagans", CVAR_SERVERINFO | CVAR_ARCHIVE); + Cvar_Get ("color1", "4", CVAR_USERINFO | CVAR_ARCHIVE ); + Cvar_Get ("color2", "5", CVAR_USERINFO | CVAR_ARCHIVE ); + Cvar_Get ("handicap", "100", CVAR_USERINFO | CVAR_ARCHIVE ); + Cvar_Get ("teamtask", "0", CVAR_USERINFO ); + Cvar_Get ("sex", "male", CVAR_USERINFO | CVAR_ARCHIVE ); + Cvar_Get ("cl_anonymous", "0", CVAR_USERINFO | CVAR_ARCHIVE ); + + Cvar_Get ("password", "", CVAR_USERINFO); + Cvar_Get ("cg_predictItems", "1", CVAR_USERINFO | CVAR_ARCHIVE ); + +#ifdef USE_MUMBLE + cl_useMumble = Cvar_Get ("cl_useMumble", "0", CVAR_ARCHIVE | CVAR_LATCH); + cl_mumbleScale = Cvar_Get ("cl_mumbleScale", "0.0254", CVAR_ARCHIVE); +#endif + +#ifdef USE_VOIP + cl_voipSend = Cvar_Get ("cl_voipSend", "0", 0); + cl_voipSendTarget = Cvar_Get ("cl_voipSendTarget", "all", 0); + cl_voipGainDuringCapture = Cvar_Get ("cl_voipGainDuringCapture", "0.2", CVAR_ARCHIVE); + cl_voipCaptureMult = Cvar_Get ("cl_voipCaptureMult", "2.0", CVAR_ARCHIVE); + cl_voipUseVAD = Cvar_Get ("cl_voipUseVAD", "0", CVAR_ARCHIVE); + cl_voipVADThreshold = Cvar_Get ("cl_voipVADThreshold", "0.25", CVAR_ARCHIVE); + cl_voipShowMeter = Cvar_Get ("cl_voipShowMeter", "1", CVAR_ARCHIVE); + + // This is a protocol version number. + cl_voip = Cvar_Get ("cl_voip", "1", CVAR_USERINFO | CVAR_ARCHIVE | CVAR_LATCH); + Cvar_CheckRange( cl_voip, 0, 1, qtrue ); + + // If your data rate is too low, you'll get Connection Interrupted warnings + // when VoIP packets arrive, even if you have a broadband connection. + // This might work on rates lower than 25000, but for safety's sake, we'll + // just demand it. Who doesn't have at least a DSL line now, anyhow? If + // you don't, you don't need VoIP. :) + if ((cl_voip->integer) && (Cvar_VariableIntegerValue("rate") < 25000)) { + Com_Printf("Your network rate is too slow for VoIP.\n"); + Com_Printf("Set 'Data Rate' to 'LAN/Cable/xDSL' in 'Setup/System/Network' and restart.\n"); + Com_Printf("Until then, VoIP is disabled.\n"); + Cvar_Set("cl_voip", "0"); + } +#endif + + + // cgame might not be initialized before menu is used + Cvar_Get ("cg_viewsize", "100", CVAR_ARCHIVE ); + // Make sure cg_stereoSeparation is zero as that variable is deprecated and should not be used anymore. + Cvar_Get ("cg_stereoSeparation", "0", CVAR_ROM); + + // + // register our commands + // + Cmd_AddCommand ("cmd", CL_ForwardToServer_f); + Cmd_AddCommand ("configstrings", CL_Configstrings_f); + Cmd_AddCommand ("clientinfo", CL_Clientinfo_f); + Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f); + Cmd_AddCommand ("vid_restart", CL_Vid_Restart_f); + Cmd_AddCommand ("disconnect", CL_Disconnect_f); + Cmd_AddCommand ("record", CL_Record_f); + Cmd_AddCommand ("demo", CL_PlayDemo_f); + Cmd_SetCommandCompletionFunc( "demo", CL_CompleteDemoName ); + Cmd_AddCommand ("cinematic", CL_PlayCinematic_f); + Cmd_AddCommand ("stoprecord", CL_StopRecord_f); + Cmd_AddCommand ("connect", CL_Connect_f); + Cmd_AddCommand ("reconnect", CL_Reconnect_f); + Cmd_AddCommand ("localservers", CL_LocalServers_f); + Cmd_AddCommand ("globalservers", CL_GlobalServers_f); + Cmd_AddCommand ("rcon", CL_Rcon_f); + Cmd_SetCommandCompletionFunc( "rcon", CL_CompleteRcon ); + Cmd_AddCommand ("setenv", CL_Setenv_f ); + Cmd_AddCommand ("ping", CL_Ping_f ); + Cmd_AddCommand ("serverstatus", CL_ServerStatus_f ); + Cmd_AddCommand ("showip", CL_ShowIP_f ); + Cmd_AddCommand ("fs_openedList", CL_OpenedPK3List_f ); + Cmd_AddCommand ("fs_referencedList", CL_ReferencedPK3List_f ); + Cmd_AddCommand ("model", CL_SetModel_f ); + Cmd_AddCommand ("video", CL_Video_f ); + Cmd_AddCommand ("stopvideo", CL_StopVideo_f ); + CL_InitRef(); + + SCR_Init (); + +// Cbuf_Execute (); + + Cvar_Set( "cl_running", "1" ); + + CL_GenerateQKey(); + Cvar_Get( "cl_guid", "", CVAR_USERINFO | CVAR_ROM ); + CL_UpdateGUID( NULL, 0 ); + + Com_Printf( "----- Client Initialization Complete -----\n" ); +} + + +/* +=============== +CL_Shutdown + +=============== +*/ +void CL_Shutdown( void ) { + static qboolean recursive = qfalse; + + // check whether the client is running at all. + if(!(com_cl_running && com_cl_running->integer)) + return; + + Com_Printf( "----- CL_Shutdown -----\n" ); + + if ( recursive ) { + Com_Printf( "WARNING: Recursive shutdown\n" ); + return; + } + recursive = qtrue; + + CL_Disconnect( qtrue ); + + S_Shutdown(); + CL_ShutdownRef(); + + CL_ShutdownUI(); + + Cmd_RemoveCommand ("cmd"); + Cmd_RemoveCommand ("configstrings"); + Cmd_RemoveCommand ("userinfo"); + Cmd_RemoveCommand ("snd_restart"); + Cmd_RemoveCommand ("vid_restart"); + Cmd_RemoveCommand ("disconnect"); + Cmd_RemoveCommand ("record"); + Cmd_RemoveCommand ("demo"); + Cmd_RemoveCommand ("cinematic"); + Cmd_RemoveCommand ("stoprecord"); + Cmd_RemoveCommand ("connect"); + Cmd_RemoveCommand ("localservers"); + Cmd_RemoveCommand ("globalservers"); + Cmd_RemoveCommand ("rcon"); + Cmd_RemoveCommand ("setenv"); + Cmd_RemoveCommand ("ping"); + Cmd_RemoveCommand ("serverstatus"); + Cmd_RemoveCommand ("showip"); + Cmd_RemoveCommand ("model"); + Cmd_RemoveCommand ("video"); + Cmd_RemoveCommand ("stopvideo"); + + Cvar_Set( "cl_running", "0" ); + + recursive = qfalse; + + Com_Memset( &cls, 0, sizeof( cls ) ); + Key_SetCatcher( 0 ); + + Com_Printf( "-----------------------\n" ); + +} + +static void CL_SetServerInfo(serverInfo_t *server, const char *info, int ping) { + if (server) { + if (info) { + server->clients = atoi(Info_ValueForKey(info, "clients")); + Q_strncpyz(server->hostName,Info_ValueForKey(info, "hostname"), MAX_NAME_LENGTH); + Q_strncpyz(server->mapName, Info_ValueForKey(info, "mapname"), MAX_NAME_LENGTH); + server->maxClients = atoi(Info_ValueForKey(info, "sv_maxclients")); + Q_strncpyz(server->game,Info_ValueForKey(info, "game"), MAX_NAME_LENGTH); + server->gameType = atoi(Info_ValueForKey(info, "gametype")); + server->netType = atoi(Info_ValueForKey(info, "nettype")); + server->minPing = atoi(Info_ValueForKey(info, "minping")); + server->maxPing = atoi(Info_ValueForKey(info, "maxping")); + server->punkbuster = atoi(Info_ValueForKey(info, "punkbuster")); + } + server->ping = ping; + } +} + +static void CL_SetServerInfoByAddress(netadr_t from, const char *info, int ping) { + int i; + + for (i = 0; i < MAX_OTHER_SERVERS; i++) { + if (NET_CompareAdr(from, cls.localServers[i].adr)) { + CL_SetServerInfo(&cls.localServers[i], info, ping); + } + } + + for (i = 0; i < MAX_GLOBAL_SERVERS; i++) { + if (NET_CompareAdr(from, cls.globalServers[i].adr)) { + CL_SetServerInfo(&cls.globalServers[i], info, ping); + } + } + + for (i = 0; i < MAX_OTHER_SERVERS; i++) { + if (NET_CompareAdr(from, cls.favoriteServers[i].adr)) { + CL_SetServerInfo(&cls.favoriteServers[i], info, ping); + } + } + +} + +/* +=================== +CL_ServerInfoPacket +=================== +*/ +void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) { + int i, type; + char info[MAX_INFO_STRING]; + char *infoString; + int prot; + + infoString = MSG_ReadString( msg ); + + // if this isn't the correct protocol version, ignore it + prot = atoi( Info_ValueForKey( infoString, "protocol" ) ); + if ( prot != PROTOCOL_VERSION ) { + Com_DPrintf( "Different protocol info packet: %s\n", infoString ); + return; + } + + // iterate servers waiting for ping response + for (i=0; iretrieved = qtrue; + return qfalse; + } + + // if this server status request has the same address + if ( NET_CompareAdr( to, serverStatus->address) ) { + // if we recieved an response for this server status request + if (!serverStatus->pending) { + Q_strncpyz(serverStatusString, serverStatus->string, maxLen); + serverStatus->retrieved = qtrue; + serverStatus->startTime = 0; + return qtrue; + } + // resend the request regularly + else if ( serverStatus->startTime < Com_Milliseconds() - cl_serverStatusResendTime->integer ) { + serverStatus->print = qfalse; + serverStatus->pending = qtrue; + serverStatus->retrieved = qfalse; + serverStatus->time = 0; + serverStatus->startTime = Com_Milliseconds(); + NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" ); + return qfalse; + } + } + // if retrieved + else if ( serverStatus->retrieved ) { + serverStatus->address = to; + serverStatus->print = qfalse; + serverStatus->pending = qtrue; + serverStatus->retrieved = qfalse; + serverStatus->startTime = Com_Milliseconds(); + serverStatus->time = 0; + NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" ); + return qfalse; + } + return qfalse; +} + +/* +=================== +CL_ServerStatusResponse +=================== +*/ +void CL_ServerStatusResponse( netadr_t from, msg_t *msg ) { + char *s; + char info[MAX_INFO_STRING]; + int i, l, score, ping; + int len; + serverStatus_t *serverStatus; + + serverStatus = NULL; + for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) { + if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) { + serverStatus = &cl_serverStatusList[i]; + break; + } + } + // if we didn't request this server status + if (!serverStatus) { + return; + } + + s = MSG_ReadStringLine( msg ); + + len = 0; + Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "%s", s); + + if (serverStatus->print) { + Com_Printf("Server settings:\n"); + // print cvars + while (*s) { + for (i = 0; i < 2 && *s; i++) { + if (*s == '\\') + s++; + l = 0; + while (*s) { + info[l++] = *s; + if (l >= MAX_INFO_STRING-1) + break; + s++; + if (*s == '\\') { + break; + } + } + info[l] = '\0'; + if (i) { + Com_Printf("%s\n", info); + } + else { + Com_Printf("%-24s", info); + } + } + } + } + + len = strlen(serverStatus->string); + Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\"); + + if (serverStatus->print) { + Com_Printf("\nPlayers:\n"); + Com_Printf("num: score: ping: name:\n"); + } + for (i = 0, s = MSG_ReadStringLine( msg ); *s; s = MSG_ReadStringLine( msg ), i++) { + + len = strlen(serverStatus->string); + Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\%s", s); + + if (serverStatus->print) { + score = ping = 0; + sscanf(s, "%d %d", &score, &ping); + s = strchr(s, ' '); + if (s) + s = strchr(s+1, ' '); + if (s) + s++; + else + s = "unknown"; + Com_Printf("%-2d %-3d %-3d %s\n", i, score, ping, s ); + } + } + len = strlen(serverStatus->string); + Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\"); + + serverStatus->time = Com_Milliseconds(); + serverStatus->address = from; + serverStatus->pending = qfalse; + if (serverStatus->print) { + serverStatus->retrieved = qtrue; + } +} + +/* +================== +CL_LocalServers_f +================== +*/ +void CL_LocalServers_f( void ) { + char *message; + int i, j; + netadr_t to; + + Com_Printf( "Scanning for servers on the local network...\n"); + + // reset the list, waiting for response + cls.numlocalservers = 0; + cls.pingUpdateSource = AS_LOCAL; + + for (i = 0; i < MAX_OTHER_SERVERS; i++) { + qboolean b = cls.localServers[i].visible; + Com_Memset(&cls.localServers[i], 0, sizeof(cls.localServers[i])); + cls.localServers[i].visible = b; + } + Com_Memset( &to, 0, sizeof( to ) ); + + // The 'xxx' in the message is a challenge that will be echoed back + // by the server. We don't care about that here, but master servers + // can use that to prevent spoofed server responses from invalid ip + message = "\377\377\377\377getinfo xxx"; + + // send each message twice in case one is dropped + for ( i = 0 ; i < 2 ; i++ ) { + // send a broadcast packet on each server port + // we support multiple server ports so a single machine + // can nicely run multiple servers + for ( j = 0 ; j < NUM_SERVER_PORTS ; j++ ) { + to.port = BigShort( (short)(PORT_SERVER + j) ); + + to.type = NA_BROADCAST; + NET_SendPacket( NS_CLIENT, strlen( message ), message, to ); + to.type = NA_MULTICAST6; + NET_SendPacket( NS_CLIENT, strlen( message ), message, to ); + } + } +} + +/* +================== +CL_GlobalServers_f +================== +*/ +void CL_GlobalServers_f( void ) { + netadr_t to; + int count, i, masterNum; + char command[1024], *masteraddress; + char *cmdname; + + if ((count = Cmd_Argc()) < 3 || (masterNum = atoi(Cmd_Argv(1))) < 0 || masterNum > 4) + { + Com_Printf( "usage: globalservers [keywords]\n"); + return; + } + + sprintf(command, "sv_master%d", masterNum + 1); + masteraddress = Cvar_VariableString(command); + + if(!*masteraddress) + { + Com_Printf( "CL_GlobalServers_f: Error: No master server address given.\n"); + return; + } + + // reset the list, waiting for response + // -1 is used to distinguish a "no response" + + i = NET_StringToAdr(masteraddress, &to, NA_UNSPEC); + + if(!i) + { + Com_Printf( "CL_GlobalServers_f: Error: could not resolve address of master %s\n", masteraddress); + return; + } + else if(i == 2) + to.port = BigShort(PORT_MASTER); + + Com_Printf("Requesting servers from master %s...\n", masteraddress); + + cls.numglobalservers = -1; + cls.pingUpdateSource = AS_GLOBAL; + + // Use the extended query for IPv6 masters + if (to.type == NA_IP6 || to.type == NA_MULTICAST6) + { + cmdname = "getserversExt " GAMENAME_FOR_MASTER; + + // TODO: test if we only have an IPv6 connection. If it's the case, + // request IPv6 servers only by appending " ipv6" to the command + } + else + cmdname = "getservers"; + Com_sprintf( command, sizeof(command), "%s %s", cmdname, Cmd_Argv(2) ); + + for (i=3; i < count; i++) + { + Q_strcat(command, sizeof(command), " "); + Q_strcat(command, sizeof(command), Cmd_Argv(i)); + } + + NET_OutOfBandPrint( NS_SERVER, to, "%s", command ); +} + + +/* +================== +CL_GetPing +================== +*/ +void CL_GetPing( int n, char *buf, int buflen, int *pingtime ) +{ + const char *str; + int time; + int maxPing; + + if (!cl_pinglist[n].adr.port) + { + // empty slot + buf[0] = '\0'; + *pingtime = 0; + return; + } + + str = NET_AdrToStringwPort( cl_pinglist[n].adr ); + Q_strncpyz( buf, str, buflen ); + + time = cl_pinglist[n].time; + if (!time) + { + // check for timeout + time = cls.realtime - cl_pinglist[n].start; + maxPing = Cvar_VariableIntegerValue( "cl_maxPing" ); + if( maxPing < 100 ) { + maxPing = 100; + } + if (time < maxPing) + { + // not timed out yet + time = 0; + } + } + + CL_SetServerInfoByAddress(cl_pinglist[n].adr, cl_pinglist[n].info, cl_pinglist[n].time); + + *pingtime = time; +} + +/* +================== +CL_UpdateServerInfo +================== +*/ +void CL_UpdateServerInfo( int n ) +{ + if (!cl_pinglist[n].adr.port) + { + return; + } + + CL_SetServerInfoByAddress(cl_pinglist[n].adr, cl_pinglist[n].info, cl_pinglist[n].time ); +} + +/* +================== +CL_GetPingInfo +================== +*/ +void CL_GetPingInfo( int n, char *buf, int buflen ) +{ + if (!cl_pinglist[n].adr.port) + { + // empty slot + if (buflen) + buf[0] = '\0'; + return; + } + + Q_strncpyz( buf, cl_pinglist[n].info, buflen ); +} + +/* +================== +CL_ClearPing +================== +*/ +void CL_ClearPing( int n ) +{ + if (n < 0 || n >= MAX_PINGREQUESTS) + return; + + cl_pinglist[n].adr.port = 0; +} + +/* +================== +CL_GetPingQueueCount +================== +*/ +int CL_GetPingQueueCount( void ) +{ + int i; + int count; + ping_t* pingptr; + + count = 0; + pingptr = cl_pinglist; + + for (i=0; iadr.port) { + count++; + } + } + + return (count); +} + +/* +================== +CL_GetFreePing +================== +*/ +ping_t* CL_GetFreePing( void ) +{ + ping_t* pingptr; + ping_t* best; + int oldest; + int i; + int time; + + pingptr = cl_pinglist; + for (i=0; iadr.port) + { + if (!pingptr->time) + { + if (cls.realtime - pingptr->start < 500) + { + // still waiting for response + continue; + } + } + else if (pingptr->time < 500) + { + // results have not been queried + continue; + } + } + + // clear it + pingptr->adr.port = 0; + return (pingptr); + } + + // use oldest entry + pingptr = cl_pinglist; + best = cl_pinglist; + oldest = INT_MIN; + for (i=0; istart; + if (time > oldest) + { + oldest = time; + best = pingptr; + } + } + + return (best); +} + +/* +================== +CL_Ping_f +================== +*/ +void CL_Ping_f( void ) { + netadr_t to; + ping_t* pingptr; + char* server; + int argc; + netadrtype_t family = NA_UNSPEC; + + argc = Cmd_Argc(); + + if ( argc != 2 && argc != 3 ) { + Com_Printf( "usage: ping [-4|-6] server\n"); + return; + } + + if(argc == 2) + server = Cmd_Argv(1); + else + { + if(!strcmp(Cmd_Argv(1), "-4")) + family = NA_IP; + else if(!strcmp(Cmd_Argv(1), "-6")) + family = NA_IP6; + else + Com_Printf( "warning: only -4 or -6 as address type understood.\n"); + + server = Cmd_Argv(2); + } + + Com_Memset( &to, 0, sizeof(netadr_t) ); + + if ( !NET_StringToAdr( server, &to, family ) ) { + return; + } + + pingptr = CL_GetFreePing(); + + memcpy( &pingptr->adr, &to, sizeof (netadr_t) ); + pingptr->start = cls.realtime; + pingptr->time = 0; + + CL_SetServerInfoByAddress(pingptr->adr, NULL, 0); + + NET_OutOfBandPrint( NS_CLIENT, to, "getinfo xxx" ); +} + +/* +================== +CL_UpdateVisiblePings_f +================== +*/ +qboolean CL_UpdateVisiblePings_f(int source) { + int slots, i; + char buff[MAX_STRING_CHARS]; + int pingTime; + int max; + qboolean status = qfalse; + + if (source < 0 || source > AS_FAVORITES) { + return qfalse; + } + + cls.pingUpdateSource = source; + + slots = CL_GetPingQueueCount(); + if (slots < MAX_PINGREQUESTS) { + serverInfo_t *server = NULL; + + max = (source == AS_GLOBAL) ? MAX_GLOBAL_SERVERS : MAX_OTHER_SERVERS; + switch (source) { + case AS_LOCAL : + server = &cls.localServers[0]; + max = cls.numlocalservers; + break; + case AS_GLOBAL : + server = &cls.globalServers[0]; + max = cls.numglobalservers; + break; + case AS_FAVORITES : + server = &cls.favoriteServers[0]; + max = cls.numfavoriteservers; + break; + default: + return qfalse; + } + for (i = 0; i < max; i++) { + if (server[i].visible) { + if (server[i].ping == -1) { + int j; + + if (slots >= MAX_PINGREQUESTS) { + break; + } + for (j = 0; j < MAX_PINGREQUESTS; j++) { + if (!cl_pinglist[j].adr.port) { + continue; + } + if (NET_CompareAdr( cl_pinglist[j].adr, server[i].adr)) { + // already on the list + break; + } + } + if (j >= MAX_PINGREQUESTS) { + status = qtrue; + for (j = 0; j < MAX_PINGREQUESTS; j++) { + if (!cl_pinglist[j].adr.port) { + break; + } + } + memcpy(&cl_pinglist[j].adr, &server[i].adr, sizeof(netadr_t)); + cl_pinglist[j].start = cls.realtime; + cl_pinglist[j].time = 0; + NET_OutOfBandPrint( NS_CLIENT, cl_pinglist[j].adr, "getinfo xxx" ); + slots++; + } + } + // if the server has a ping higher than cl_maxPing or + // the ping packet got lost + else if (server[i].ping == 0) { + // if we are updating global servers + if (source == AS_GLOBAL) { + // + if ( cls.numGlobalServerAddresses > 0 ) { + // overwrite this server with one from the additional global servers + cls.numGlobalServerAddresses--; + CL_InitServerInfo(&server[i], &cls.globalServerAddresses[cls.numGlobalServerAddresses]); + // NOTE: the server[i].visible flag stays untouched + } + } + } + } + } + } + + if (slots) { + status = qtrue; + } + for (i = 0; i < MAX_PINGREQUESTS; i++) { + if (!cl_pinglist[i].adr.port) { + continue; + } + CL_GetPing( i, buff, MAX_STRING_CHARS, &pingTime ); + if (pingTime != 0) { + CL_ClearPing(i); + status = qtrue; + } + } + + return status; +} + +/* +================== +CL_ServerStatus_f +================== +*/ +void CL_ServerStatus_f(void) { + netadr_t to, *toptr = NULL; + char *server; + serverStatus_t *serverStatus; + int argc; + netadrtype_t family = NA_UNSPEC; + + argc = Cmd_Argc(); + + if ( argc != 2 && argc != 3 ) + { + if (cls.state != CA_ACTIVE || clc.demoplaying) + { + Com_Printf ("Not connected to a server.\n"); + Com_Printf( "usage: serverstatus [-4|-6] server\n"); + return; + } + + toptr = &clc.serverAddress; + } + + if(!toptr) + { + Com_Memset( &to, 0, sizeof(netadr_t) ); + + if(argc == 2) + server = Cmd_Argv(1); + else + { + if(!strcmp(Cmd_Argv(1), "-4")) + family = NA_IP; + else if(!strcmp(Cmd_Argv(1), "-6")) + family = NA_IP6; + else + Com_Printf( "warning: only -4 or -6 as address type understood.\n"); + + server = Cmd_Argv(2); + } + + toptr = &to; + if ( !NET_StringToAdr( server, toptr, family ) ) + return; + } + + NET_OutOfBandPrint( NS_CLIENT, *toptr, "getstatus" ); + + serverStatus = CL_GetServerStatus( *toptr ); + serverStatus->address = *toptr; + serverStatus->print = qtrue; + serverStatus->pending = qtrue; +} + +/* +================== +CL_ShowIP_f +================== +*/ +void CL_ShowIP_f(void) { + Sys_ShowIP(); +} + +#ifndef STANDALONE +/* +================= +bool CL_CDKeyValidate +================= +*/ +qboolean CL_CDKeyValidate( const char *key, const char *checksum ) { + char ch; + byte sum; + char chs[3]; + int i, len; + + len = strlen(key); + if( len != CDKEY_LEN ) { + return qfalse; + } + + if( checksum && strlen( checksum ) != CDCHKSUM_LEN ) { + return qfalse; + } + + sum = 0; + // for loop gets rid of conditional assignment warning + for (i = 0; i < len; i++) { + ch = *key++; + if (ch>='a' && ch<='z') { + ch -= 32; + } + switch( ch ) { + case '2': + case '3': + case '7': + case 'A': + case 'B': + case 'C': + case 'D': + case 'G': + case 'H': + case 'J': + case 'L': + case 'P': + case 'R': + case 'S': + case 'T': + case 'W': + sum += ch; + continue; + default: + return qfalse; + } + } + + sprintf(chs, "%02x", sum); + + if (checksum && !Q_stricmp(chs, checksum)) { + return qtrue; + } + + if (!checksum) { + return qtrue; + } + + return qfalse; +} +#endif diff --git a/reaction/engine/code/client/cl_net_chan.c b/reaction/engine/code/client/cl_net_chan.c new file mode 100644 index 00000000..1433c434 --- /dev/null +++ b/reaction/engine/code/client/cl_net_chan.c @@ -0,0 +1,167 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "client.h" + +/* +============== +CL_Netchan_Encode + + // first 12 bytes of the data are always: + long serverId; + long messageAcknowledge; + long reliableAcknowledge; + +============== +*/ +static void CL_Netchan_Encode( msg_t *msg ) { + int serverId, messageAcknowledge, reliableAcknowledge; + int i, index, srdc, sbit, soob; + byte key, *string; + + if ( msg->cursize <= CL_ENCODE_START ) { + return; + } + + srdc = msg->readcount; + sbit = msg->bit; + soob = msg->oob; + + msg->bit = 0; + msg->readcount = 0; + msg->oob = 0; + + serverId = MSG_ReadLong(msg); + messageAcknowledge = MSG_ReadLong(msg); + reliableAcknowledge = MSG_ReadLong(msg); + + msg->oob = soob; + msg->bit = sbit; + msg->readcount = srdc; + + string = (byte *)clc.serverCommands[ reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ]; + index = 0; + // + key = clc.challenge ^ serverId ^ messageAcknowledge; + for (i = CL_ENCODE_START; i < msg->cursize; i++) { + // modify the key with the last received now acknowledged server command + if (!string[index]) + index = 0; + if (string[index] > 127 || string[index] == '%') { + key ^= '.' << (i & 1); + } + else { + key ^= string[index] << (i & 1); + } + index++; + // encode the data with this key + *(msg->data + i) = (*(msg->data + i)) ^ key; + } +} + +/* +============== +CL_Netchan_Decode + + // first four bytes of the data are always: + long reliableAcknowledge; + +============== +*/ +static void CL_Netchan_Decode( msg_t *msg ) { + long reliableAcknowledge, i, index; + byte key, *string; + int srdc, sbit, soob; + + srdc = msg->readcount; + sbit = msg->bit; + soob = msg->oob; + + msg->oob = 0; + + reliableAcknowledge = MSG_ReadLong(msg); + + msg->oob = soob; + msg->bit = sbit; + msg->readcount = srdc; + + string = (byte *) clc.reliableCommands[ reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ]; + index = 0; + // xor the client challenge with the netchan sequence number (need something that changes every message) + key = clc.challenge ^ LittleLong( *(unsigned *)msg->data ); + for (i = msg->readcount + CL_DECODE_START; i < msg->cursize; i++) { + // modify the key with the last sent and with this message acknowledged client command + if (!string[index]) + index = 0; + if (string[index] > 127 || string[index] == '%') { + key ^= '.' << (i & 1); + } + else { + key ^= string[index] << (i & 1); + } + index++; + // decode the data with this key + *(msg->data + i) = *(msg->data + i) ^ key; + } +} + +/* +================= +CL_Netchan_TransmitNextFragment +================= +*/ +void CL_Netchan_TransmitNextFragment( netchan_t *chan ) { + Netchan_TransmitNextFragment( chan ); +} + +/* +=============== +CL_Netchan_Transmit +================ +*/ +void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) { + MSG_WriteByte( msg, clc_EOF ); + + CL_Netchan_Encode( msg ); + Netchan_Transmit( chan, msg->cursize, msg->data ); +} + +extern int oldsize; +int newsize = 0; + +/* +================= +CL_Netchan_Process +================= +*/ +qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ) { + int ret; + + ret = Netchan_Process( chan, msg ); + if (!ret) + return qfalse; + CL_Netchan_Decode( msg ); + newsize += msg->cursize; + return qtrue; +} diff --git a/reaction/engine/code/client/cl_parse.c b/reaction/engine/code/client/cl_parse.c new file mode 100644 index 00000000..3c4b4fbd --- /dev/null +++ b/reaction/engine/code/client/cl_parse.c @@ -0,0 +1,911 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// cl_parse.c -- parse a message received from the server + +#include "client.h" + +char *svc_strings[256] = { + "svc_bad", + + "svc_nop", + "svc_gamestate", + "svc_configstring", + "svc_baseline", + "svc_serverCommand", + "svc_download", + "svc_snapshot", + "svc_EOF", + "svc_extension", + "svc_voip", +}; + +void SHOWNET( msg_t *msg, char *s) { + if ( cl_shownet->integer >= 2) { + Com_Printf ("%3i:%s\n", msg->readcount-1, s); + } +} + + +/* +========================================================================= + +MESSAGE PARSING + +========================================================================= +*/ + +/* +================== +CL_DeltaEntity + +Parses deltas from the given base and adds the resulting entity +to the current frame +================== +*/ +void CL_DeltaEntity (msg_t *msg, clSnapshot_t *frame, int newnum, entityState_t *old, + qboolean unchanged) { + entityState_t *state; + + // save the parsed entity state into the big circular buffer so + // it can be used as the source for a later delta + state = &cl.parseEntities[cl.parseEntitiesNum & (MAX_PARSE_ENTITIES-1)]; + + if ( unchanged ) { + *state = *old; + } else { + MSG_ReadDeltaEntity( msg, old, state, newnum ); + } + + if ( state->number == (MAX_GENTITIES-1) ) { + return; // entity was delta removed + } + cl.parseEntitiesNum++; + frame->numEntities++; +} + +/* +================== +CL_ParsePacketEntities + +================== +*/ +void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe) { + int newnum; + entityState_t *oldstate; + int oldindex, oldnum; + + newframe->parseEntitiesNum = cl.parseEntitiesNum; + newframe->numEntities = 0; + + // delta from the entities present in oldframe + oldindex = 0; + oldstate = NULL; + if (!oldframe) { + oldnum = 99999; + } else { + if ( oldindex >= oldframe->numEntities ) { + oldnum = 99999; + } else { + oldstate = &cl.parseEntities[ + (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; + oldnum = oldstate->number; + } + } + + while ( 1 ) { + // read the entity index number + newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); + + if ( newnum == (MAX_GENTITIES-1) ) { + break; + } + + if ( msg->readcount > msg->cursize ) { + Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message"); + } + + while ( oldnum < newnum ) { + // one or more entities from the old packet are unchanged + if ( cl_shownet->integer == 3 ) { + Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum); + } + CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue ); + + oldindex++; + + if ( oldindex >= oldframe->numEntities ) { + oldnum = 99999; + } else { + oldstate = &cl.parseEntities[ + (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; + oldnum = oldstate->number; + } + } + if (oldnum == newnum) { + // delta from previous state + if ( cl_shownet->integer == 3 ) { + Com_Printf ("%3i: delta: %i\n", msg->readcount, newnum); + } + CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse ); + + oldindex++; + + if ( oldindex >= oldframe->numEntities ) { + oldnum = 99999; + } else { + oldstate = &cl.parseEntities[ + (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; + oldnum = oldstate->number; + } + continue; + } + + if ( oldnum > newnum ) { + // delta from baseline + if ( cl_shownet->integer == 3 ) { + Com_Printf ("%3i: baseline: %i\n", msg->readcount, newnum); + } + CL_DeltaEntity( msg, newframe, newnum, &cl.entityBaselines[newnum], qfalse ); + continue; + } + + } + + // any remaining entities in the old frame are copied over + while ( oldnum != 99999 ) { + // one or more entities from the old packet are unchanged + if ( cl_shownet->integer == 3 ) { + Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum); + } + CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue ); + + oldindex++; + + if ( oldindex >= oldframe->numEntities ) { + oldnum = 99999; + } else { + oldstate = &cl.parseEntities[ + (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; + oldnum = oldstate->number; + } + } +} + + +/* +================ +CL_ParseSnapshot + +If the snapshot is parsed properly, it will be copied to +cl.snap and saved in cl.snapshots[]. If the snapshot is invalid +for any reason, no changes to the state will be made at all. +================ +*/ +void CL_ParseSnapshot( msg_t *msg ) { + int len; + clSnapshot_t *old; + clSnapshot_t newSnap; + int deltaNum; + int oldMessageNum; + int i, packetNum; + + // get the reliable sequence acknowledge number + // NOTE: now sent with all server to client messages + //clc.reliableAcknowledge = MSG_ReadLong( msg ); + + // read in the new snapshot to a temporary buffer + // we will only copy to cl.snap if it is valid + Com_Memset (&newSnap, 0, sizeof(newSnap)); + + // we will have read any new server commands in this + // message before we got to svc_snapshot + newSnap.serverCommandNum = clc.serverCommandSequence; + + newSnap.serverTime = MSG_ReadLong( msg ); + + // if we were just unpaused, we can only *now* really let the + // change come into effect or the client hangs. + cl_paused->modified = 0; + + newSnap.messageNum = clc.serverMessageSequence; + + deltaNum = MSG_ReadByte( msg ); + if ( !deltaNum ) { + newSnap.deltaNum = -1; + } else { + newSnap.deltaNum = newSnap.messageNum - deltaNum; + } + newSnap.snapFlags = MSG_ReadByte( msg ); + + // If the frame is delta compressed from data that we + // no longer have available, we must suck up the rest of + // the frame, but not use it, then ask for a non-compressed + // message + if ( newSnap.deltaNum <= 0 ) { + newSnap.valid = qtrue; // uncompressed frame + old = NULL; + clc.demowaiting = qfalse; // we can start recording now + } else { + old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK]; + if ( !old->valid ) { + // should never happen + Com_Printf ("Delta from invalid frame (not supposed to happen!).\n"); + } else if ( old->messageNum != newSnap.deltaNum ) { + // The frame that the server did the delta from + // is too old, so we can't reconstruct it properly. + Com_Printf ("Delta frame too old.\n"); + } else if ( cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES-128 ) { + Com_Printf ("Delta parseEntitiesNum too old.\n"); + } else { + newSnap.valid = qtrue; // valid delta parse + } + } + + // read areamask + len = MSG_ReadByte( msg ); + + if(len > sizeof(newSnap.areamask)) + { + Com_Error (ERR_DROP,"CL_ParseSnapshot: Invalid size %d for areamask.", len); + return; + } + + MSG_ReadData( msg, &newSnap.areamask, len); + + // read playerinfo + SHOWNET( msg, "playerstate" ); + if ( old ) { + MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps ); + } else { + MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps ); + } + + // read packet entities + SHOWNET( msg, "packet entities" ); + CL_ParsePacketEntities( msg, old, &newSnap ); + + // if not valid, dump the entire thing now that it has + // been properly read + if ( !newSnap.valid ) { + return; + } + + // clear the valid flags of any snapshots between the last + // received and this one, so if there was a dropped packet + // it won't look like something valid to delta from next + // time we wrap around in the buffer + oldMessageNum = cl.snap.messageNum + 1; + + if ( newSnap.messageNum - oldMessageNum >= PACKET_BACKUP ) { + oldMessageNum = newSnap.messageNum - ( PACKET_BACKUP - 1 ); + } + for ( ; oldMessageNum < newSnap.messageNum ; oldMessageNum++ ) { + cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse; + } + + // copy to the current good spot + cl.snap = newSnap; + cl.snap.ping = 999; + // calculate ping time + for ( i = 0 ; i < PACKET_BACKUP ; i++ ) { + packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK; + if ( cl.snap.ps.commandTime >= cl.outPackets[ packetNum ].p_serverTime ) { + cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime; + break; + } + } + // save the frame off in the backup array for later delta comparisons + cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap; + + if (cl_shownet->integer == 3) { + Com_Printf( " snapshot:%i delta:%i ping:%i\n", cl.snap.messageNum, + cl.snap.deltaNum, cl.snap.ping ); + } + + cl.newSnapshots = qtrue; +} + + +//===================================================================== + +int cl_connectedToPureServer; +int cl_connectedToCheatServer; + +#ifdef USE_VOIP +int cl_connectedToVoipServer; +#endif + +/* +================== +CL_SystemInfoChanged + +The systeminfo configstring has been changed, so parse +new information out of it. This will happen at every +gamestate, and possibly during gameplay. +================== +*/ +void CL_SystemInfoChanged( void ) { + char *systemInfo; + const char *s, *t; + char key[BIG_INFO_KEY]; + char value[BIG_INFO_VALUE]; + qboolean gameSet; + + systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SYSTEMINFO ]; + // NOTE TTimo: + // when the serverId changes, any further messages we send to the server will use this new serverId + // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475 + // in some cases, outdated cp commands might get sent with this news serverId + cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) ); + + // don't set any vars when playing a demo + if ( clc.demoplaying ) { + return; + } + +#ifdef USE_VOIP + // in the future, (val) will be a protocol version string, so only + // accept explicitly 1, not generally non-zero. + s = Info_ValueForKey( systemInfo, "sv_voip" ); + cl_connectedToVoipServer = (atoi( s ) == 1); +#endif + + s = Info_ValueForKey( systemInfo, "sv_cheats" ); + cl_connectedToCheatServer = atoi( s ); + if ( !cl_connectedToCheatServer ) { + Cvar_SetCheatState(); + } + + // check pure server string + s = Info_ValueForKey( systemInfo, "sv_paks" ); + t = Info_ValueForKey( systemInfo, "sv_pakNames" ); + FS_PureServerSetLoadedPaks( s, t ); + + s = Info_ValueForKey( systemInfo, "sv_referencedPaks" ); + t = Info_ValueForKey( systemInfo, "sv_referencedPakNames" ); + FS_PureServerSetReferencedPaks( s, t ); + + gameSet = qfalse; + // scan through all the variables in the systeminfo and locally set cvars to match + s = systemInfo; + while ( s ) { + int cvar_flags; + + Info_NextPair( &s, key, value ); + if ( !key[0] ) { + break; + } + + // ehw! + if (!Q_stricmp(key, "fs_game")) + { + if(FS_CheckDirTraversal(value)) + { + Com_Printf(S_COLOR_YELLOW "WARNING: Server sent invalid fs_game value %s\n", value); + continue; + } + + gameSet = qtrue; + } + + if((cvar_flags = Cvar_Flags(key)) == CVAR_NONEXISTENT) + Cvar_Get(key, value, CVAR_SERVER_CREATED | CVAR_ROM); + else + { + // If this cvar may not be modified by a server discard the value. + if(!(cvar_flags & (CVAR_SYSTEMINFO | CVAR_SERVER_CREATED))) + { + Com_Printf(S_COLOR_YELLOW "WARNING: server is not allowed to set %s=%s\n", key, value); + continue; + } + + Cvar_Set(key, value); + } + } + // if game folder should not be set and it is set at the client side + if ( !gameSet && *Cvar_VariableString("fs_game") ) { + Cvar_Set( "fs_game", "" ); + } + cl_connectedToPureServer = Cvar_VariableValue( "sv_pure" ); +} + +/* +================== +CL_ParseServerInfo +================== +*/ +static void CL_ParseServerInfo(void) +{ + const char *serverInfo; + + serverInfo = cl.gameState.stringData + + cl.gameState.stringOffsets[ CS_SERVERINFO ]; + + clc.sv_allowDownload = atoi(Info_ValueForKey(serverInfo, + "sv_allowDownload")); + Q_strncpyz(clc.sv_dlURL, + Info_ValueForKey(serverInfo, "sv_dlURL"), + sizeof(clc.sv_dlURL)); +} + +/* +================== +CL_ParseGamestate +================== +*/ +void CL_ParseGamestate( msg_t *msg ) { + int i; + entityState_t *es; + int newnum; + entityState_t nullstate; + int cmd; + char *s; + + Con_Close(); + + clc.connectPacketCount = 0; + + // wipe local client state + CL_ClearState(); + + // a gamestate always marks a server command sequence + clc.serverCommandSequence = MSG_ReadLong( msg ); + + // parse all the configstrings and baselines + cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings + while ( 1 ) { + cmd = MSG_ReadByte( msg ); + + if ( cmd == svc_EOF ) { + break; + } + + if ( cmd == svc_configstring ) { + int len; + + i = MSG_ReadShort( msg ); + if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { + Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); + } + s = MSG_ReadBigString( msg ); + len = strlen( s ); + + if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { + Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); + } + + // append it to the gameState string buffer + cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; + Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 ); + cl.gameState.dataCount += len + 1; + } else if ( cmd == svc_baseline ) { + newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); + if ( newnum < 0 || newnum >= MAX_GENTITIES ) { + Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); + } + Com_Memset (&nullstate, 0, sizeof(nullstate)); + es = &cl.entityBaselines[ newnum ]; + MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); + } else { + Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); + } + } + + clc.clientNum = MSG_ReadLong(msg); + // read the checksum feed + clc.checksumFeed = MSG_ReadLong( msg ); + + // parse useful values out of CS_SERVERINFO + CL_ParseServerInfo(); + + // parse serverId and other cvars + CL_SystemInfoChanged(); + + // stop recording now so the demo won't have an unnecessary level load at the end. + if(cl_autoRecordDemo->integer && clc.demorecording) + CL_StopRecord_f(); + + // reinitialize the filesystem if the game directory has changed + FS_ConditionalRestart( clc.checksumFeed ); + + // This used to call CL_StartHunkUsers, but now we enter the download state before loading the + // cgame + CL_InitDownloads(); + + // make sure the game starts + Cvar_Set( "cl_paused", "0" ); +} + + +//===================================================================== + +/* +===================== +CL_ParseDownload + +A download message has been received from the server +===================== +*/ +void CL_ParseDownload ( msg_t *msg ) { + int size; + unsigned char data[MAX_MSGLEN]; + int block; + + if (!*clc.downloadTempName) { + Com_Printf("Server sending download, but no download was requested\n"); + CL_AddReliableCommand( "stopdl" ); + return; + } + + // read the data + block = MSG_ReadShort ( msg ); + + if ( !block ) + { + // block zero is special, contains file size + clc.downloadSize = MSG_ReadLong ( msg ); + + Cvar_SetValue( "cl_downloadSize", clc.downloadSize ); + + if (clc.downloadSize < 0) + { + Com_Error( ERR_DROP, "%s", MSG_ReadString( msg ) ); + return; + } + } + + size = MSG_ReadShort ( msg ); + if (size < 0 || size > sizeof(data)) + { + Com_Error(ERR_DROP, "CL_ParseDownload: Invalid size %d for download chunk.", size); + return; + } + + MSG_ReadData(msg, data, size); + + if (clc.downloadBlock != block) { + Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", clc.downloadBlock, block); + return; + } + + // open the file if not opened yet + if (!clc.download) + { + clc.download = FS_SV_FOpenFileWrite( clc.downloadTempName ); + + if (!clc.download) { + Com_Printf( "Could not create %s\n", clc.downloadTempName ); + CL_AddReliableCommand( "stopdl" ); + CL_NextDownload(); + return; + } + } + + if (size) + FS_Write( data, size, clc.download ); + + CL_AddReliableCommand( va("nextdl %d", clc.downloadBlock) ); + clc.downloadBlock++; + + clc.downloadCount += size; + + // So UI gets access to it + Cvar_SetValue( "cl_downloadCount", clc.downloadCount ); + + if (!size) { // A zero length block means EOF + if (clc.download) { + FS_FCloseFile( clc.download ); + clc.download = 0; + + // rename the file + FS_SV_Rename ( clc.downloadTempName, clc.downloadName ); + } + *clc.downloadTempName = *clc.downloadName = 0; + Cvar_Set( "cl_downloadName", "" ); + + // send intentions now + // We need this because without it, we would hold the last nextdl and then start + // loading right away. If we take a while to load, the server is happily trying + // to send us that last block over and over. + // Write it twice to help make sure we acknowledge the download + CL_WritePacket(); + CL_WritePacket(); + + // get another file if needed + CL_NextDownload (); + } +} + +#ifdef USE_VOIP +static +qboolean CL_ShouldIgnoreVoipSender(int sender) +{ + if (!cl_voip->integer) + return qtrue; // VoIP is disabled. + else if ((sender == clc.clientNum) && (!clc.demoplaying)) + return qtrue; // ignore own voice (unless playing back a demo). + else if (clc.voipMuteAll) + return qtrue; // all channels are muted with extreme prejudice. + else if (clc.voipIgnore[sender]) + return qtrue; // just ignoring this guy. + else if (clc.voipGain[sender] == 0.0f) + return qtrue; // too quiet to play. + + return qfalse; +} + +/* +===================== +CL_ParseVoip + +A VoIP message has been received from the server +===================== +*/ +static +void CL_ParseVoip ( msg_t *msg ) { + static short decoded[4096]; // !!! FIXME: don't hardcode. + + const int sender = MSG_ReadShort(msg); + const int generation = MSG_ReadByte(msg); + const int sequence = MSG_ReadLong(msg); + const int frames = MSG_ReadByte(msg); + const int packetsize = MSG_ReadShort(msg); + char encoded[1024]; + int seqdiff = sequence - clc.voipIncomingSequence[sender]; + int written = 0; + int i; + + Com_DPrintf("VoIP: %d-byte packet from client %d\n", packetsize, sender); + + if (sender < 0) + return; // short/invalid packet, bail. + else if (generation < 0) + return; // short/invalid packet, bail. + else if (sequence < 0) + return; // short/invalid packet, bail. + else if (frames < 0) + return; // short/invalid packet, bail. + else if (packetsize < 0) + return; // short/invalid packet, bail. + + if (packetsize > sizeof (encoded)) { // overlarge packet? + int bytesleft = packetsize; + while (bytesleft) { + int br = bytesleft; + if (br > sizeof (encoded)) + br = sizeof (encoded); + MSG_ReadData(msg, encoded, br); + bytesleft -= br; + } + return; // overlarge packet, bail. + } + + if (!clc.speexInitialized) { + MSG_ReadData(msg, encoded, packetsize); // skip payload. + return; // can't handle VoIP without libspeex! + } else if (sender >= MAX_CLIENTS) { + MSG_ReadData(msg, encoded, packetsize); // skip payload. + return; // bogus sender. + } else if (CL_ShouldIgnoreVoipSender(sender)) { + MSG_ReadData(msg, encoded, packetsize); // skip payload. + return; // Channel is muted, bail. + } + + // !!! FIXME: make sure data is narrowband? Does decoder handle this? + + Com_DPrintf("VoIP: packet accepted!\n"); + + // This is a new "generation" ... a new recording started, reset the bits. + if (generation != clc.voipIncomingGeneration[sender]) { + Com_DPrintf("VoIP: new generation %d!\n", generation); + speex_bits_reset(&clc.speexDecoderBits[sender]); + clc.voipIncomingGeneration[sender] = generation; + seqdiff = 0; + } else if (seqdiff < 0) { // we're ahead of the sequence?! + // This shouldn't happen unless the packet is corrupted or something. + Com_DPrintf("VoIP: misordered sequence! %d < %d!\n", + sequence, clc.voipIncomingSequence[sender]); + // reset the bits just in case. + speex_bits_reset(&clc.speexDecoderBits[sender]); + seqdiff = 0; + } else if (seqdiff > 100) { // more than 2 seconds of audio dropped? + // just start over. + Com_DPrintf("VoIP: Dropped way too many (%d) frames from client #%d\n", + seqdiff, sender); + speex_bits_reset(&clc.speexDecoderBits[sender]); + seqdiff = 0; + } + + if (seqdiff != 0) { + Com_DPrintf("VoIP: Dropped %d frames from client #%d\n", + seqdiff, sender); + // tell speex that we're missing frames... + for (i = 0; i < seqdiff; i++) { + assert((written + clc.speexFrameSize) * 2 < sizeof (decoded)); + speex_decode_int(clc.speexDecoder[sender], NULL, decoded + written); + written += clc.speexFrameSize; + } + } + + for (i = 0; i < frames; i++) { + char encoded[256]; + const int len = MSG_ReadByte(msg); + if (len < 0) { + Com_DPrintf("VoIP: Short packet!\n"); + break; + } + MSG_ReadData(msg, encoded, len); + + // shouldn't happen, but just in case... + if ((written + clc.speexFrameSize) * 2 > sizeof (decoded)) { + Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n", + written * 2, written, i); + S_RawSamples(sender + 1, written, clc.speexSampleRate, 2, 1, + (const byte *) decoded, clc.voipGain[sender]); + written = 0; + } + + speex_bits_read_from(&clc.speexDecoderBits[sender], encoded, len); + speex_decode_int(clc.speexDecoder[sender], + &clc.speexDecoderBits[sender], decoded + written); + + #if 0 + static FILE *encio = NULL; + if (encio == NULL) encio = fopen("voip-incoming-encoded.bin", "wb"); + if (encio != NULL) { fwrite(encoded, len, 1, encio); fflush(encio); } + static FILE *decio = NULL; + if (decio == NULL) decio = fopen("voip-incoming-decoded.bin", "wb"); + if (decio != NULL) { fwrite(decoded+written, clc.speexFrameSize*2, 1, decio); fflush(decio); } + #endif + + written += clc.speexFrameSize; + } + + Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n", + written * 2, written, i); + + if (written > 0) { + S_RawSamples(sender + 1, written, clc.speexSampleRate, 2, 1, + (const byte *) decoded, clc.voipGain[sender]); + } + + clc.voipIncomingSequence[sender] = sequence + frames; +} +#endif + + +/* +===================== +CL_ParseCommandString + +Command strings are just saved off until cgame asks for them +when it transitions a snapshot +===================== +*/ +void CL_ParseCommandString( msg_t *msg ) { + char *s; + int seq; + int index; + + seq = MSG_ReadLong( msg ); + s = MSG_ReadString( msg ); + + // see if we have already executed stored it off + if ( clc.serverCommandSequence >= seq ) { + return; + } + clc.serverCommandSequence = seq; + + index = seq & (MAX_RELIABLE_COMMANDS-1); + Q_strncpyz( clc.serverCommands[ index ], s, sizeof( clc.serverCommands[ index ] ) ); +} + + +/* +===================== +CL_ParseServerMessage +===================== +*/ +void CL_ParseServerMessage( msg_t *msg ) { + int cmd; + + if ( cl_shownet->integer == 1 ) { + Com_Printf ("%i ",msg->cursize); + } else if ( cl_shownet->integer >= 2 ) { + Com_Printf ("------------------\n"); + } + + MSG_Bitstream(msg); + + // get the reliable sequence acknowledge number + clc.reliableAcknowledge = MSG_ReadLong( msg ); + // + if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) { + clc.reliableAcknowledge = clc.reliableSequence; + } + + // + // parse the message + // + while ( 1 ) { + if ( msg->readcount > msg->cursize ) { + Com_Error (ERR_DROP,"CL_ParseServerMessage: read past end of server message"); + break; + } + + cmd = MSG_ReadByte( msg ); + + // See if this is an extension command after the EOF, which means we + // got data that a legacy client should ignore. + if ((cmd == svc_EOF) && (MSG_LookaheadByte( msg ) == svc_extension)) { + SHOWNET( msg, "EXTENSION" ); + MSG_ReadByte( msg ); // throw the svc_extension byte away. + cmd = MSG_ReadByte( msg ); // something legacy clients can't do! + // sometimes you get a svc_extension at end of stream...dangling + // bits in the huffman decoder giving a bogus value? + if (cmd == -1) { + cmd = svc_EOF; + } + } + + if (cmd == svc_EOF) { + SHOWNET( msg, "END OF MESSAGE" ); + break; + } + + if ( cl_shownet->integer >= 2 ) { + if ( (cmd < 0) || (!svc_strings[cmd]) ) { + Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd ); + } else { + SHOWNET( msg, svc_strings[cmd] ); + } + } + + // other commands + switch ( cmd ) { + default: + Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n"); + break; + case svc_nop: + break; + case svc_serverCommand: + CL_ParseCommandString( msg ); + break; + case svc_gamestate: + CL_ParseGamestate( msg ); + break; + case svc_snapshot: + CL_ParseSnapshot( msg ); + break; + case svc_download: + CL_ParseDownload( msg ); + break; + case svc_voip: +#ifdef USE_VOIP + CL_ParseVoip( msg ); +#endif + break; + } + } +} + + diff --git a/reaction/engine/code/client/cl_scrn.c b/reaction/engine/code/client/cl_scrn.c new file mode 100644 index 00000000..732913fc --- /dev/null +++ b/reaction/engine/code/client/cl_scrn.c @@ -0,0 +1,595 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// cl_scrn.c -- master for refresh, status bar, console, chat, notify, etc + +#include "client.h" + +qboolean scr_initialized; // ready to draw + +cvar_t *cl_timegraph; +cvar_t *cl_debuggraph; +cvar_t *cl_graphheight; +cvar_t *cl_graphscale; +cvar_t *cl_graphshift; + +/* +================ +SCR_DrawNamedPic + +Coordinates are 640*480 virtual values +================= +*/ +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ) { + qhandle_t hShader; + + assert( width != 0 ); + + hShader = re.RegisterShader( picname ); + SCR_AdjustFrom640( &x, &y, &width, &height ); + re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader ); +} + + +/* +================ +SCR_AdjustFrom640 + +Adjusted for resolution and screen aspect ratio +================ +*/ +void SCR_AdjustFrom640( float *x, float *y, float *w, float *h ) { + float xscale; + float yscale; + +#if 0 + // adjust for wide screens + if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) { + *x += 0.5 * ( cls.glconfig.vidWidth - ( cls.glconfig.vidHeight * 640 / 480 ) ); + } +#endif + + // scale for screen sizes + xscale = cls.glconfig.vidWidth / 640.0; + yscale = cls.glconfig.vidHeight / 480.0; + if ( x ) { + *x *= xscale; + } + if ( y ) { + *y *= yscale; + } + if ( w ) { + *w *= xscale; + } + if ( h ) { + *h *= yscale; + } +} + +/* +================ +SCR_FillRect + +Coordinates are 640*480 virtual values +================= +*/ +void SCR_FillRect( float x, float y, float width, float height, const float *color ) { + re.SetColor( color ); + + SCR_AdjustFrom640( &x, &y, &width, &height ); + re.DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cls.whiteShader ); + + re.SetColor( NULL ); +} + + +/* +================ +SCR_DrawPic + +Coordinates are 640*480 virtual values +================= +*/ +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) { + SCR_AdjustFrom640( &x, &y, &width, &height ); + re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader ); +} + + + +/* +** SCR_DrawChar +** chars are drawn at 640*480 virtual screen size +*/ +static void SCR_DrawChar( int x, int y, float size, int ch ) { + int row, col; + float frow, fcol; + float ax, ay, aw, ah; + + ch &= 255; + + if ( ch == ' ' ) { + return; + } + + if ( y < -size ) { + return; + } + + ax = x; + ay = y; + aw = size; + ah = size; + SCR_AdjustFrom640( &ax, &ay, &aw, &ah ); + + row = ch>>4; + col = ch&15; + + frow = row*0.0625; + fcol = col*0.0625; + size = 0.0625; + + re.DrawStretchPic( ax, ay, aw, ah, + fcol, frow, + fcol + size, frow + size, + cls.charSetShader ); +} + +/* +** SCR_DrawSmallChar +** small chars are drawn at native screen resolution +*/ +void SCR_DrawSmallChar( int x, int y, int ch ) { + int row, col; + float frow, fcol; + float size; + + ch &= 255; + + if ( ch == ' ' ) { + return; + } + + if ( y < -SMALLCHAR_HEIGHT ) { + return; + } + + row = ch>>4; + col = ch&15; + + frow = row*0.0625; + fcol = col*0.0625; + size = 0.0625; + + re.DrawStretchPic( x, y, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, + fcol, frow, + fcol + size, frow + size, + cls.charSetShader ); +} + + +/* +================== +SCR_DrawBigString[Color] + +Draws a multi-colored string with a drop shadow, optionally forcing +to a fixed color. + +Coordinates are at 640 by 480 virtual resolution +================== +*/ +void SCR_DrawStringExt( int x, int y, float size, const char *string, float *setColor, qboolean forceColor, + qboolean noColorEscape ) { + vec4_t color; + const char *s; + int xx; + + // draw the drop shadow + color[0] = color[1] = color[2] = 0; + color[3] = setColor[3]; + re.SetColor( color ); + s = string; + xx = x; + while ( *s ) { + if ( !noColorEscape && Q_IsColorString( s ) ) { + s += 2; + continue; + } + SCR_DrawChar( xx+2, y+2, size, *s ); + xx += size; + s++; + } + + + // draw the colored text + s = string; + xx = x; + re.SetColor( setColor ); + while ( *s ) { + if ( !noColorEscape && Q_IsColorString( s ) ) { + if ( !forceColor ) { + Com_Memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) ); + color[3] = setColor[3]; + re.SetColor( color ); + } + s += 2; + continue; + } + SCR_DrawChar( xx, y, size, *s ); + xx += size; + s++; + } + re.SetColor( NULL ); +} + + +void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ) { + float color[4]; + + color[0] = color[1] = color[2] = 1.0; + color[3] = alpha; + SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qfalse, noColorEscape ); +} + +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ) { + SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qtrue, noColorEscape ); +} + + +/* +================== +SCR_DrawSmallString[Color] + +Draws a multi-colored string with a drop shadow, optionally forcing +to a fixed color. +================== +*/ +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor, + qboolean noColorEscape ) { + vec4_t color; + const char *s; + int xx; + + // draw the colored text + s = string; + xx = x; + re.SetColor( setColor ); + while ( *s ) { + if ( !noColorEscape && Q_IsColorString( s ) ) { + if ( !forceColor ) { + Com_Memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) ); + color[3] = setColor[3]; + re.SetColor( color ); + } + s += 2; + continue; + } + SCR_DrawSmallChar( xx, y, *s ); + xx += SMALLCHAR_WIDTH; + s++; + } + re.SetColor( NULL ); +} + + + +/* +** SCR_Strlen -- skips color escape codes +*/ +static int SCR_Strlen( const char *str ) { + const char *s = str; + int count = 0; + + while ( *s ) { + if ( Q_IsColorString( s ) ) { + s += 2; + } else { + count++; + s++; + } + } + + return count; +} + +/* +** SCR_GetBigStringWidth +*/ +int SCR_GetBigStringWidth( const char *str ) { + return SCR_Strlen( str ) * 16; +} + + +//=============================================================================== + +/* +================= +SCR_DrawDemoRecording +================= +*/ +void SCR_DrawDemoRecording( void ) { + char string[1024]; + int pos; + + if ( !clc.demorecording ) { + return; + } + if ( clc.spDemoRecording ) { + return; + } + + pos = FS_FTell( clc.demofile ); + sprintf( string, "RECORDING %s: %ik", clc.demoName, pos / 1024 ); + + SCR_DrawStringExt( 320 - strlen( string ) * 4, 20, 8, string, g_color_table[7], qtrue, qfalse ); +} + + +#ifdef USE_VOIP +/* +================= +SCR_DrawVoipMeter +================= +*/ +void SCR_DrawVoipMeter( void ) { + char buffer[16]; + char string[256]; + int limit, i; + + if (!cl_voipShowMeter->integer) + return; // player doesn't want to show meter at all. + else if (!cl_voipSend->integer) + return; // not recording at the moment. + else if (cls.state != CA_ACTIVE) + return; // not connected to a server. + else if (!cl_connectedToVoipServer) + return; // server doesn't support VoIP. + else if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) + return; // single player game. + else if (clc.demoplaying) + return; // playing back a demo. + else if (!cl_voip->integer) + return; // client has VoIP support disabled. + + limit = (int) (clc.voipPower * 10.0f); + if (limit > 10) + limit = 10; + + for (i = 0; i < limit; i++) + buffer[i] = '*'; + while (i < 10) + buffer[i++] = ' '; + buffer[i] = '\0'; + + sprintf( string, "VoIP: [%s]", buffer ); + SCR_DrawStringExt( 320 - strlen( string ) * 4, 10, 8, string, g_color_table[7], qtrue, qfalse ); +} +#endif + + + + +/* +=============================================================================== + +DEBUG GRAPH + +=============================================================================== +*/ + +typedef struct +{ + float value; + int color; +} graphsamp_t; + +static int current; +static graphsamp_t values[1024]; + +/* +============== +SCR_DebugGraph +============== +*/ +void SCR_DebugGraph (float value, int color) +{ + values[current&1023].value = value; + values[current&1023].color = color; + current++; +} + +/* +============== +SCR_DrawDebugGraph +============== +*/ +void SCR_DrawDebugGraph (void) +{ + int a, x, y, w, i, h; + float v; + int color; + + // + // draw the graph + // + w = cls.glconfig.vidWidth; + x = 0; + y = cls.glconfig.vidHeight; + re.SetColor( g_color_table[0] ); + re.DrawStretchPic(x, y - cl_graphheight->integer, + w, cl_graphheight->integer, 0, 0, 0, 0, cls.whiteShader ); + re.SetColor( NULL ); + + for (a=0 ; ainteger + cl_graphshift->integer; + + if (v < 0) + v += cl_graphheight->integer * (1+(int)(-v / cl_graphheight->integer)); + h = (int)v % cl_graphheight->integer; + re.DrawStretchPic( x+w-1-a, y - h, 1, h, 0, 0, 0, 0, cls.whiteShader ); + } +} + +//============================================================================= + +/* +================== +SCR_Init +================== +*/ +void SCR_Init( void ) { + cl_timegraph = Cvar_Get ("timegraph", "0", CVAR_CHEAT); + cl_debuggraph = Cvar_Get ("debuggraph", "0", CVAR_CHEAT); + cl_graphheight = Cvar_Get ("graphheight", "32", CVAR_CHEAT); + cl_graphscale = Cvar_Get ("graphscale", "1", CVAR_CHEAT); + cl_graphshift = Cvar_Get ("graphshift", "0", CVAR_CHEAT); + + scr_initialized = qtrue; +} + + +//======================================================= + +/* +================== +SCR_DrawScreenField + +This will be called twice if rendering in stereo mode +================== +*/ +void SCR_DrawScreenField( stereoFrame_t stereoFrame ) { + re.BeginFrame( stereoFrame ); + + // wide aspect ratio screens need to have the sides cleared + // unless they are displaying game renderings + if ( cls.state != CA_ACTIVE && cls.state != CA_CINEMATIC ) { + if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) { + re.SetColor( g_color_table[0] ); + re.DrawStretchPic( 0, 0, cls.glconfig.vidWidth, cls.glconfig.vidHeight, 0, 0, 0, 0, cls.whiteShader ); + re.SetColor( NULL ); + } + } + + // if the menu is going to cover the entire screen, we + // don't need to render anything under it + if ( uivm && !VM_Call( uivm, UI_IS_FULLSCREEN )) { + switch( cls.state ) { + default: + Com_Error( ERR_FATAL, "SCR_DrawScreenField: bad cls.state" ); + break; + case CA_CINEMATIC: + SCR_DrawCinematic(); + break; + case CA_DISCONNECTED: + // force menu up + S_StopAllSounds(); + VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN ); + break; + case CA_CONNECTING: + case CA_CHALLENGING: + case CA_CONNECTED: + // connecting clients will only show the connection dialog + // refresh to update the time + VM_Call( uivm, UI_REFRESH, cls.realtime ); + VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qfalse ); + break; + case CA_LOADING: + case CA_PRIMED: + // draw the game information screen and loading progress + CL_CGameRendering(stereoFrame); + + // also draw the connection information, so it doesn't + // flash away too briefly on local or lan games + // refresh to update the time + VM_Call( uivm, UI_REFRESH, cls.realtime ); + VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qtrue ); + break; + case CA_ACTIVE: + // always supply STEREO_CENTER as vieworg offset is now done by the engine. + CL_CGameRendering(stereoFrame); + SCR_DrawDemoRecording(); +#ifdef USE_VOIP + SCR_DrawVoipMeter(); +#endif + break; + } + } + + // the menu draws next + if ( Key_GetCatcher( ) & KEYCATCH_UI && uivm ) { + VM_Call( uivm, UI_REFRESH, cls.realtime ); + } + + // console draws next + Con_DrawConsole (); + + // debug graph can be drawn on top of anything + if ( cl_debuggraph->integer || cl_timegraph->integer || cl_debugMove->integer ) { + SCR_DrawDebugGraph (); + } +} + +/* +================== +SCR_UpdateScreen + +This is called every frame, and can also be called explicitly to flush +text to the screen. +================== +*/ +void SCR_UpdateScreen( void ) { + static int recursive; + + if ( !scr_initialized ) { + return; // not initialized yet + } + + if ( ++recursive > 2 ) { + Com_Error( ERR_FATAL, "SCR_UpdateScreen: recursively called" ); + } + recursive = 1; + + // If there is no VM, there are also no rendering commands issued. Stop the renderer in + // that case. + if( uivm || com_dedicated->integer ) + { + // if running in stereo, we need to draw the frame twice + if ( cls.glconfig.stereoEnabled || Cvar_VariableIntegerValue("r_anaglyphMode")) { + SCR_DrawScreenField( STEREO_LEFT ); + SCR_DrawScreenField( STEREO_RIGHT ); + } else { + SCR_DrawScreenField( STEREO_CENTER ); + } + + if ( com_speeds->integer ) { + re.EndFrame( &time_frontend, &time_backend ); + } else { + re.EndFrame( NULL, NULL ); + } + } + + recursive = 0; +} + diff --git a/reaction/engine/code/client/cl_ui.c b/reaction/engine/code/client/cl_ui.c new file mode 100644 index 00000000..ed32313b --- /dev/null +++ b/reaction/engine/code/client/cl_ui.c @@ -0,0 +1,1154 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "client.h" + +#include "../botlib/botlib.h" + +extern botlib_export_t *botlib_export; + +vm_t *uivm; + +/* +==================== +GetClientState +==================== +*/ +static void GetClientState( uiClientState_t *state ) { + state->connectPacketCount = clc.connectPacketCount; + state->connState = cls.state; + Q_strncpyz( state->servername, cls.servername, sizeof( state->servername ) ); + Q_strncpyz( state->updateInfoString, cls.updateInfoString, sizeof( state->updateInfoString ) ); + Q_strncpyz( state->messageString, clc.serverMessage, sizeof( state->messageString ) ); + state->clientNum = cl.snap.ps.clientNum; +} + +/* +==================== +LAN_LoadCachedServers +==================== +*/ +void LAN_LoadCachedServers( void ) { + int size; + fileHandle_t fileIn; + cls.numglobalservers = cls.numfavoriteservers = 0; + cls.numGlobalServerAddresses = 0; + if (FS_SV_FOpenFileRead("servercache.dat", &fileIn)) { + FS_Read(&cls.numglobalservers, sizeof(int), fileIn); + FS_Read(&cls.numfavoriteservers, sizeof(int), fileIn); + FS_Read(&size, sizeof(int), fileIn); + if (size == sizeof(cls.globalServers) + sizeof(cls.favoriteServers)) { + FS_Read(&cls.globalServers, sizeof(cls.globalServers), fileIn); + FS_Read(&cls.favoriteServers, sizeof(cls.favoriteServers), fileIn); + } else { + cls.numglobalservers = cls.numfavoriteservers = 0; + cls.numGlobalServerAddresses = 0; + } + FS_FCloseFile(fileIn); + } +} + +/* +==================== +LAN_SaveServersToCache +==================== +*/ +void LAN_SaveServersToCache( void ) { + int size; + fileHandle_t fileOut = FS_SV_FOpenFileWrite("servercache.dat"); + FS_Write(&cls.numglobalservers, sizeof(int), fileOut); + FS_Write(&cls.numfavoriteservers, sizeof(int), fileOut); + size = sizeof(cls.globalServers) + sizeof(cls.favoriteServers); + FS_Write(&size, sizeof(int), fileOut); + FS_Write(&cls.globalServers, sizeof(cls.globalServers), fileOut); + FS_Write(&cls.favoriteServers, sizeof(cls.favoriteServers), fileOut); + FS_FCloseFile(fileOut); +} + + +/* +==================== +LAN_ResetPings +==================== +*/ +static void LAN_ResetPings(int source) { + int count,i; + serverInfo_t *servers = NULL; + count = 0; + + switch (source) { + case AS_LOCAL : + servers = &cls.localServers[0]; + count = MAX_OTHER_SERVERS; + break; + case AS_MPLAYER: + case AS_GLOBAL : + servers = &cls.globalServers[0]; + count = MAX_GLOBAL_SERVERS; + break; + case AS_FAVORITES : + servers = &cls.favoriteServers[0]; + count = MAX_OTHER_SERVERS; + break; + } + if (servers) { + for (i = 0; i < count; i++) { + servers[i].ping = -1; + } + } +} + +/* +==================== +LAN_AddServer +==================== +*/ +static int LAN_AddServer(int source, const char *name, const char *address) { + int max, *count, i; + netadr_t adr; + serverInfo_t *servers = NULL; + max = MAX_OTHER_SERVERS; + count = NULL; + + switch (source) { + case AS_LOCAL : + count = &cls.numlocalservers; + servers = &cls.localServers[0]; + break; + case AS_MPLAYER: + case AS_GLOBAL : + max = MAX_GLOBAL_SERVERS; + count = &cls.numglobalservers; + servers = &cls.globalServers[0]; + break; + case AS_FAVORITES : + count = &cls.numfavoriteservers; + servers = &cls.favoriteServers[0]; + break; + } + if (servers && *count < max) { + NET_StringToAdr( address, &adr, NA_IP ); + for ( i = 0; i < *count; i++ ) { + if (NET_CompareAdr(servers[i].adr, adr)) { + break; + } + } + if (i >= *count) { + servers[*count].adr = adr; + Q_strncpyz(servers[*count].hostName, name, sizeof(servers[*count].hostName)); + servers[*count].visible = qtrue; + (*count)++; + return 1; + } + return 0; + } + return -1; +} + +/* +==================== +LAN_RemoveServer +==================== +*/ +static void LAN_RemoveServer(int source, const char *addr) { + int *count, i; + serverInfo_t *servers = NULL; + count = NULL; + switch (source) { + case AS_LOCAL : + count = &cls.numlocalservers; + servers = &cls.localServers[0]; + break; + case AS_MPLAYER: + case AS_GLOBAL : + count = &cls.numglobalservers; + servers = &cls.globalServers[0]; + break; + case AS_FAVORITES : + count = &cls.numfavoriteservers; + servers = &cls.favoriteServers[0]; + break; + } + if (servers) { + netadr_t comp; + NET_StringToAdr( addr, &comp, NA_IP ); + for (i = 0; i < *count; i++) { + if (NET_CompareAdr( comp, servers[i].adr)) { + int j = i; + while (j < *count - 1) { + Com_Memcpy(&servers[j], &servers[j+1], sizeof(servers[j])); + j++; + } + (*count)--; + break; + } + } + } +} + + +/* +==================== +LAN_GetServerCount +==================== +*/ +static int LAN_GetServerCount( int source ) { + switch (source) { + case AS_LOCAL : + return cls.numlocalservers; + break; + case AS_MPLAYER: + case AS_GLOBAL : + return cls.numglobalservers; + break; + case AS_FAVORITES : + return cls.numfavoriteservers; + break; + } + return 0; +} + +/* +==================== +LAN_GetLocalServerAddressString +==================== +*/ +static void LAN_GetServerAddressString( int source, int n, char *buf, int buflen ) { + switch (source) { + case AS_LOCAL : + if (n >= 0 && n < MAX_OTHER_SERVERS) { + Q_strncpyz(buf, NET_AdrToStringwPort( cls.localServers[n].adr) , buflen ); + return; + } + break; + case AS_MPLAYER: + case AS_GLOBAL : + if (n >= 0 && n < MAX_GLOBAL_SERVERS) { + Q_strncpyz(buf, NET_AdrToStringwPort( cls.globalServers[n].adr) , buflen ); + return; + } + break; + case AS_FAVORITES : + if (n >= 0 && n < MAX_OTHER_SERVERS) { + Q_strncpyz(buf, NET_AdrToStringwPort( cls.favoriteServers[n].adr) , buflen ); + return; + } + break; + } + buf[0] = '\0'; +} + +/* +==================== +LAN_GetServerInfo +==================== +*/ +static void LAN_GetServerInfo( int source, int n, char *buf, int buflen ) { + char info[MAX_STRING_CHARS]; + serverInfo_t *server = NULL; + info[0] = '\0'; + switch (source) { + case AS_LOCAL : + if (n >= 0 && n < MAX_OTHER_SERVERS) { + server = &cls.localServers[n]; + } + break; + case AS_MPLAYER: + case AS_GLOBAL : + if (n >= 0 && n < MAX_GLOBAL_SERVERS) { + server = &cls.globalServers[n]; + } + break; + case AS_FAVORITES : + if (n >= 0 && n < MAX_OTHER_SERVERS) { + server = &cls.favoriteServers[n]; + } + break; + } + if (server && buf) { + buf[0] = '\0'; + Info_SetValueForKey( info, "hostname", server->hostName); + Info_SetValueForKey( info, "mapname", server->mapName); + Info_SetValueForKey( info, "clients", va("%i",server->clients)); + Info_SetValueForKey( info, "sv_maxclients", va("%i",server->maxClients)); + Info_SetValueForKey( info, "ping", va("%i",server->ping)); + Info_SetValueForKey( info, "minping", va("%i",server->minPing)); + Info_SetValueForKey( info, "maxping", va("%i",server->maxPing)); + Info_SetValueForKey( info, "game", server->game); + Info_SetValueForKey( info, "gametype", va("%i",server->gameType)); + Info_SetValueForKey( info, "nettype", va("%i",server->netType)); + Info_SetValueForKey( info, "addr", NET_AdrToStringwPort(server->adr)); + Info_SetValueForKey( info, "punkbuster", va("%i", server->punkbuster)); + Q_strncpyz(buf, info, buflen); + } else { + if (buf) { + buf[0] = '\0'; + } + } +} + +/* +==================== +LAN_GetServerPing +==================== +*/ +static int LAN_GetServerPing( int source, int n ) { + serverInfo_t *server = NULL; + switch (source) { + case AS_LOCAL : + if (n >= 0 && n < MAX_OTHER_SERVERS) { + server = &cls.localServers[n]; + } + break; + case AS_MPLAYER: + case AS_GLOBAL : + if (n >= 0 && n < MAX_GLOBAL_SERVERS) { + server = &cls.globalServers[n]; + } + break; + case AS_FAVORITES : + if (n >= 0 && n < MAX_OTHER_SERVERS) { + server = &cls.favoriteServers[n]; + } + break; + } + if (server) { + return server->ping; + } + return -1; +} + +/* +==================== +LAN_GetServerPtr +==================== +*/ +static serverInfo_t *LAN_GetServerPtr( int source, int n ) { + switch (source) { + case AS_LOCAL : + if (n >= 0 && n < MAX_OTHER_SERVERS) { + return &cls.localServers[n]; + } + break; + case AS_MPLAYER: + case AS_GLOBAL : + if (n >= 0 && n < MAX_GLOBAL_SERVERS) { + return &cls.globalServers[n]; + } + break; + case AS_FAVORITES : + if (n >= 0 && n < MAX_OTHER_SERVERS) { + return &cls.favoriteServers[n]; + } + break; + } + return NULL; +} + +/* +==================== +LAN_CompareServers +==================== +*/ +static int LAN_CompareServers( int source, int sortKey, int sortDir, int s1, int s2 ) { + int res; + serverInfo_t *server1, *server2; + + server1 = LAN_GetServerPtr(source, s1); + server2 = LAN_GetServerPtr(source, s2); + if (!server1 || !server2) { + return 0; + } + + res = 0; + switch( sortKey ) { + case SORT_HOST: + res = Q_stricmp( server1->hostName, server2->hostName ); + break; + + case SORT_MAP: + res = Q_stricmp( server1->mapName, server2->mapName ); + break; + case SORT_CLIENTS: + if (server1->clients < server2->clients) { + res = -1; + } + else if (server1->clients > server2->clients) { + res = 1; + } + else { + res = 0; + } + break; + case SORT_GAME: + if (server1->gameType < server2->gameType) { + res = -1; + } + else if (server1->gameType > server2->gameType) { + res = 1; + } + else { + res = 0; + } + break; + case SORT_PING: + if (server1->ping < server2->ping) { + res = -1; + } + else if (server1->ping > server2->ping) { + res = 1; + } + else { + res = 0; + } + break; + } + + if (sortDir) { + if (res < 0) + return 1; + if (res > 0) + return -1; + return 0; + } + return res; +} + +/* +==================== +LAN_GetPingQueueCount +==================== +*/ +static int LAN_GetPingQueueCount( void ) { + return (CL_GetPingQueueCount()); +} + +/* +==================== +LAN_ClearPing +==================== +*/ +static void LAN_ClearPing( int n ) { + CL_ClearPing( n ); +} + +/* +==================== +LAN_GetPing +==================== +*/ +static void LAN_GetPing( int n, char *buf, int buflen, int *pingtime ) { + CL_GetPing( n, buf, buflen, pingtime ); +} + +/* +==================== +LAN_GetPingInfo +==================== +*/ +static void LAN_GetPingInfo( int n, char *buf, int buflen ) { + CL_GetPingInfo( n, buf, buflen ); +} + +/* +==================== +LAN_MarkServerVisible +==================== +*/ +static void LAN_MarkServerVisible(int source, int n, qboolean visible ) { + if (n == -1) { + int count = MAX_OTHER_SERVERS; + serverInfo_t *server = NULL; + switch (source) { + case AS_LOCAL : + server = &cls.localServers[0]; + break; + case AS_MPLAYER: + case AS_GLOBAL : + server = &cls.globalServers[0]; + count = MAX_GLOBAL_SERVERS; + break; + case AS_FAVORITES : + server = &cls.favoriteServers[0]; + break; + } + if (server) { + for (n = 0; n < count; n++) { + server[n].visible = visible; + } + } + + } else { + switch (source) { + case AS_LOCAL : + if (n >= 0 && n < MAX_OTHER_SERVERS) { + cls.localServers[n].visible = visible; + } + break; + case AS_MPLAYER: + case AS_GLOBAL : + if (n >= 0 && n < MAX_GLOBAL_SERVERS) { + cls.globalServers[n].visible = visible; + } + break; + case AS_FAVORITES : + if (n >= 0 && n < MAX_OTHER_SERVERS) { + cls.favoriteServers[n].visible = visible; + } + break; + } + } +} + + +/* +======================= +LAN_ServerIsVisible +======================= +*/ +static int LAN_ServerIsVisible(int source, int n ) { + switch (source) { + case AS_LOCAL : + if (n >= 0 && n < MAX_OTHER_SERVERS) { + return cls.localServers[n].visible; + } + break; + case AS_MPLAYER: + case AS_GLOBAL : + if (n >= 0 && n < MAX_GLOBAL_SERVERS) { + return cls.globalServers[n].visible; + } + break; + case AS_FAVORITES : + if (n >= 0 && n < MAX_OTHER_SERVERS) { + return cls.favoriteServers[n].visible; + } + break; + } + return qfalse; +} + +/* +======================= +LAN_UpdateVisiblePings +======================= +*/ +qboolean LAN_UpdateVisiblePings(int source ) { + return CL_UpdateVisiblePings_f(source); +} + +/* +==================== +LAN_GetServerStatus +==================== +*/ +int LAN_GetServerStatus( char *serverAddress, char *serverStatus, int maxLen ) { + return CL_ServerStatus( serverAddress, serverStatus, maxLen ); +} + +/* +==================== +CL_GetGlConfig +==================== +*/ +static void CL_GetGlconfig( glconfig_t *config ) { + *config = cls.glconfig; +} + +/* +==================== +CL_GetClipboardData +==================== +*/ +static void CL_GetClipboardData( char *buf, int buflen ) { + char *cbd; + + cbd = Sys_GetClipboardData(); + + if ( !cbd ) { + *buf = 0; + return; + } + + Q_strncpyz( buf, cbd, buflen ); + + Z_Free( cbd ); +} + +/* +==================== +Key_KeynumToStringBuf +==================== +*/ +static void Key_KeynumToStringBuf( int keynum, char *buf, int buflen ) { + Q_strncpyz( buf, Key_KeynumToString( keynum ), buflen ); +} + +/* +==================== +Key_GetBindingBuf +==================== +*/ +static void Key_GetBindingBuf( int keynum, char *buf, int buflen ) { + char *value; + + value = Key_GetBinding( keynum ); + if ( value ) { + Q_strncpyz( buf, value, buflen ); + } + else { + *buf = 0; + } +} + +/* +==================== +CLUI_GetCDKey +==================== +*/ +#ifndef STANDALONE +static void CLUI_GetCDKey( char *buf, int buflen ) { + cvar_t *fs; + fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); + if (UI_usesUniqueCDKey() && fs && fs->string[0] != 0) { + Com_Memcpy( buf, &cl_cdkey[16], 16); + buf[16] = 0; + } else { + Com_Memcpy( buf, cl_cdkey, 16); + buf[16] = 0; + } +} + + +/* +==================== +CLUI_SetCDKey +==================== +*/ +static void CLUI_SetCDKey( char *buf ) { + cvar_t *fs; + fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); + if (UI_usesUniqueCDKey() && fs && fs->string[0] != 0) { + Com_Memcpy( &cl_cdkey[16], buf, 16 ); + cl_cdkey[32] = 0; + // set the flag so the fle will be written at the next opportunity + cvar_modifiedFlags |= CVAR_ARCHIVE; + } else { + Com_Memcpy( cl_cdkey, buf, 16 ); + // set the flag so the fle will be written at the next opportunity + cvar_modifiedFlags |= CVAR_ARCHIVE; + } +} +#endif + +/* +==================== +GetConfigString +==================== +*/ +static int GetConfigString(int index, char *buf, int size) +{ + int offset; + + if (index < 0 || index >= MAX_CONFIGSTRINGS) + return qfalse; + + offset = cl.gameState.stringOffsets[index]; + if (!offset) { + if( size ) { + buf[0] = 0; + } + return qfalse; + } + + Q_strncpyz( buf, cl.gameState.stringData+offset, size); + + return qtrue; +} + +/* +==================== +FloatAsInt +==================== +*/ +static int FloatAsInt( float f ) { + floatint_t fi; + fi.f = f; + return fi.i; +} + +/* +==================== +CL_UISystemCalls + +The ui module is making a system call +==================== +*/ +intptr_t CL_UISystemCalls( intptr_t *args ) { + switch( args[0] ) { + case UI_ERROR: + Com_Error( ERR_DROP, "%s", (const char*)VMA(1) ); + return 0; + + case UI_PRINT: + Com_Printf( "%s", (const char*)VMA(1) ); + return 0; + + case UI_MILLISECONDS: + return Sys_Milliseconds(); + + case UI_CVAR_REGISTER: + Cvar_Register( VMA(1), VMA(2), VMA(3), args[4] ); + return 0; + + case UI_CVAR_UPDATE: + Cvar_Update( VMA(1) ); + return 0; + + case UI_CVAR_SET: + Cvar_Set( VMA(1), VMA(2) ); + return 0; + + case UI_CVAR_VARIABLEVALUE: + return FloatAsInt( Cvar_VariableValue( VMA(1) ) ); + + case UI_CVAR_VARIABLESTRINGBUFFER: + Cvar_VariableStringBuffer( VMA(1), VMA(2), args[3] ); + return 0; + + case UI_CVAR_SETVALUE: + Cvar_SetValue( VMA(1), VMF(2) ); + return 0; + + case UI_CVAR_RESET: + Cvar_Reset( VMA(1) ); + return 0; + + case UI_CVAR_CREATE: + Cvar_Get( VMA(1), VMA(2), args[3] ); + return 0; + + case UI_CVAR_INFOSTRINGBUFFER: + Cvar_InfoStringBuffer( args[1], VMA(2), args[3] ); + return 0; + + case UI_ARGC: + return Cmd_Argc(); + + case UI_ARGV: + Cmd_ArgvBuffer( args[1], VMA(2), args[3] ); + return 0; + + case UI_CMD_EXECUTETEXT: + if(args[1] == 0 + && (!strncmp(VMA(2), "snd_restart", 11) + || !strncmp(VMA(2), "vid_restart", 11) + || !strncmp(VMA(2), "quit", 5))) + { + Com_Printf (S_COLOR_YELLOW "turning EXEC_NOW '%.11s' into EXEC_INSERT\n", (const char*)VMA(2)); + args[1] = EXEC_INSERT; + } + Cbuf_ExecuteText( args[1], VMA(2) ); + return 0; + + case UI_FS_FOPENFILE: + return FS_FOpenFileByMode( VMA(1), VMA(2), args[3] ); + + case UI_FS_READ: + FS_Read2( VMA(1), args[2], args[3] ); + return 0; + + case UI_FS_WRITE: + FS_Write( VMA(1), args[2], args[3] ); + return 0; + + case UI_FS_FCLOSEFILE: + FS_FCloseFile( args[1] ); + return 0; + + case UI_FS_GETFILELIST: + return FS_GetFileList( VMA(1), VMA(2), VMA(3), args[4] ); + + case UI_FS_SEEK: + return FS_Seek( args[1], args[2], args[3] ); + + case UI_R_REGISTERMODEL: + return re.RegisterModel( VMA(1) ); + + case UI_R_REGISTERSKIN: + return re.RegisterSkin( VMA(1) ); + + case UI_R_REGISTERSHADERNOMIP: + return re.RegisterShaderNoMip( VMA(1) ); + + case UI_R_CLEARSCENE: + re.ClearScene(); + return 0; + + case UI_R_ADDREFENTITYTOSCENE: + re.AddRefEntityToScene( VMA(1) ); + return 0; + + case UI_R_ADDPOLYTOSCENE: + re.AddPolyToScene( args[1], args[2], VMA(3), 1 ); + return 0; + + case UI_R_ADDLIGHTTOSCENE: + re.AddLightToScene( VMA(1), VMF(2), VMF(3), VMF(4), VMF(5) ); + return 0; + + case UI_R_RENDERSCENE: + re.RenderScene( VMA(1) ); + return 0; + + case UI_R_SETCOLOR: + re.SetColor( VMA(1) ); + return 0; + + case UI_R_DRAWSTRETCHPIC: + re.DrawStretchPic( VMF(1), VMF(2), VMF(3), VMF(4), VMF(5), VMF(6), VMF(7), VMF(8), args[9] ); + return 0; + + case UI_R_MODELBOUNDS: + re.ModelBounds( args[1], VMA(2), VMA(3) ); + return 0; + + case UI_UPDATESCREEN: + SCR_UpdateScreen(); + return 0; + + case UI_CM_LERPTAG: + re.LerpTag( VMA(1), args[2], args[3], args[4], VMF(5), VMA(6) ); + return 0; + + case UI_S_REGISTERSOUND: + return S_RegisterSound( VMA(1), args[2] ); + + case UI_S_STARTLOCALSOUND: + S_StartLocalSound( args[1], args[2] ); + return 0; + + case UI_KEY_KEYNUMTOSTRINGBUF: + Key_KeynumToStringBuf( args[1], VMA(2), args[3] ); + return 0; + + case UI_KEY_GETBINDINGBUF: + Key_GetBindingBuf( args[1], VMA(2), args[3] ); + return 0; + + case UI_KEY_SETBINDING: + Key_SetBinding( args[1], VMA(2) ); + return 0; + + case UI_KEY_ISDOWN: + return Key_IsDown( args[1] ); + + case UI_KEY_GETOVERSTRIKEMODE: + return Key_GetOverstrikeMode(); + + case UI_KEY_SETOVERSTRIKEMODE: + Key_SetOverstrikeMode( args[1] ); + return 0; + + case UI_KEY_CLEARSTATES: + Key_ClearStates(); + return 0; + + case UI_KEY_GETCATCHER: + return Key_GetCatcher(); + + case UI_KEY_SETCATCHER: + // Don't allow the ui module to close the console + Key_SetCatcher( args[1] | ( Key_GetCatcher( ) & KEYCATCH_CONSOLE ) ); + return 0; + + case UI_GETCLIPBOARDDATA: + CL_GetClipboardData( VMA(1), args[2] ); + return 0; + + case UI_GETCLIENTSTATE: + GetClientState( VMA(1) ); + return 0; + + case UI_GETGLCONFIG: + CL_GetGlconfig( VMA(1) ); + return 0; + + case UI_GETCONFIGSTRING: + return GetConfigString( args[1], VMA(2), args[3] ); + + case UI_LAN_LOADCACHEDSERVERS: + LAN_LoadCachedServers(); + return 0; + + case UI_LAN_SAVECACHEDSERVERS: + LAN_SaveServersToCache(); + return 0; + + case UI_LAN_ADDSERVER: + return LAN_AddServer(args[1], VMA(2), VMA(3)); + + case UI_LAN_REMOVESERVER: + LAN_RemoveServer(args[1], VMA(2)); + return 0; + + case UI_LAN_GETPINGQUEUECOUNT: + return LAN_GetPingQueueCount(); + + case UI_LAN_CLEARPING: + LAN_ClearPing( args[1] ); + return 0; + + case UI_LAN_GETPING: + LAN_GetPing( args[1], VMA(2), args[3], VMA(4) ); + return 0; + + case UI_LAN_GETPINGINFO: + LAN_GetPingInfo( args[1], VMA(2), args[3] ); + return 0; + + case UI_LAN_GETSERVERCOUNT: + return LAN_GetServerCount(args[1]); + + case UI_LAN_GETSERVERADDRESSSTRING: + LAN_GetServerAddressString( args[1], args[2], VMA(3), args[4] ); + return 0; + + case UI_LAN_GETSERVERINFO: + LAN_GetServerInfo( args[1], args[2], VMA(3), args[4] ); + return 0; + + case UI_LAN_GETSERVERPING: + return LAN_GetServerPing( args[1], args[2] ); + + case UI_LAN_MARKSERVERVISIBLE: + LAN_MarkServerVisible( args[1], args[2], args[3] ); + return 0; + + case UI_LAN_SERVERISVISIBLE: + return LAN_ServerIsVisible( args[1], args[2] ); + + case UI_LAN_UPDATEVISIBLEPINGS: + return LAN_UpdateVisiblePings( args[1] ); + + case UI_LAN_RESETPINGS: + LAN_ResetPings( args[1] ); + return 0; + + case UI_LAN_SERVERSTATUS: + return LAN_GetServerStatus( VMA(1), VMA(2), args[3] ); + + case UI_LAN_COMPARESERVERS: + return LAN_CompareServers( args[1], args[2], args[3], args[4], args[5] ); + + case UI_MEMORY_REMAINING: + return Hunk_MemoryRemaining(); + +#ifndef STANDALONE + case UI_GET_CDKEY: + CLUI_GetCDKey( VMA(1), args[2] ); + return 0; + + case UI_SET_CDKEY: + CLUI_SetCDKey( VMA(1) ); + return 0; +#endif + + case UI_SET_PBCLSTATUS: + return 0; + + case UI_R_REGISTERFONT: + re.RegisterFont( VMA(1), args[2], VMA(3)); + return 0; + + case UI_MEMSET: + Com_Memset( VMA(1), args[2], args[3] ); + return 0; + + case UI_MEMCPY: + Com_Memcpy( VMA(1), VMA(2), args[3] ); + return 0; + + case UI_STRNCPY: + strncpy( VMA(1), VMA(2), args[3] ); + return args[1]; + + case UI_SIN: + return FloatAsInt( sin( VMF(1) ) ); + + case UI_COS: + return FloatAsInt( cos( VMF(1) ) ); + + case UI_ATAN2: + return FloatAsInt( atan2( VMF(1), VMF(2) ) ); + + case UI_SQRT: + return FloatAsInt( sqrt( VMF(1) ) ); + + case UI_FLOOR: + return FloatAsInt( floor( VMF(1) ) ); + + case UI_CEIL: + return FloatAsInt( ceil( VMF(1) ) ); + + case UI_PC_ADD_GLOBAL_DEFINE: + return botlib_export->PC_AddGlobalDefine( VMA(1) ); + case UI_PC_LOAD_SOURCE: + return botlib_export->PC_LoadSourceHandle( VMA(1) ); + case UI_PC_FREE_SOURCE: + return botlib_export->PC_FreeSourceHandle( args[1] ); + case UI_PC_READ_TOKEN: + return botlib_export->PC_ReadTokenHandle( args[1], VMA(2) ); + case UI_PC_SOURCE_FILE_AND_LINE: + return botlib_export->PC_SourceFileAndLine( args[1], VMA(2), VMA(3) ); + + case UI_S_STOPBACKGROUNDTRACK: + S_StopBackgroundTrack(); + return 0; + case UI_S_STARTBACKGROUNDTRACK: + S_StartBackgroundTrack( VMA(1), VMA(2)); + return 0; + + case UI_REAL_TIME: + return Com_RealTime( VMA(1) ); + + case UI_CIN_PLAYCINEMATIC: + Com_DPrintf("UI_CIN_PlayCinematic\n"); + return CIN_PlayCinematic(VMA(1), args[2], args[3], args[4], args[5], args[6]); + + case UI_CIN_STOPCINEMATIC: + return CIN_StopCinematic(args[1]); + + case UI_CIN_RUNCINEMATIC: + return CIN_RunCinematic(args[1]); + + case UI_CIN_DRAWCINEMATIC: + CIN_DrawCinematic(args[1]); + return 0; + + case UI_CIN_SETEXTENTS: + CIN_SetExtents(args[1], args[2], args[3], args[4], args[5]); + return 0; + + case UI_R_REMAP_SHADER: + re.RemapShader( VMA(1), VMA(2), VMA(3) ); + return 0; + +#ifndef STANDALONE + case UI_VERIFY_CDKEY: + return CL_CDKeyValidate(VMA(1), VMA(2)); +#endif + + + default: + Com_Error( ERR_DROP, "Bad UI system trap: %ld", (long int) args[0] ); + + } + + return 0; +} + +/* +==================== +CL_ShutdownUI +==================== +*/ +void CL_ShutdownUI( void ) { + Key_SetCatcher( Key_GetCatcher( ) & ~KEYCATCH_UI ); + cls.uiStarted = qfalse; + if ( !uivm ) { + return; + } + VM_Call( uivm, UI_SHUTDOWN ); + VM_Free( uivm ); + uivm = NULL; +} + +/* +==================== +CL_InitUI +==================== +*/ +#define UI_OLD_API_VERSION 4 + +void CL_InitUI( void ) { + int v; + vmInterpret_t interpret; + + // load the dll or bytecode + if ( cl_connectedToPureServer != 0 ) { + // if sv_pure is set we only allow qvms to be loaded + interpret = VMI_COMPILED; + } + else { + interpret = Cvar_VariableValue( "vm_ui" ); + } + uivm = VM_Create( "ui", CL_UISystemCalls, interpret ); + if ( !uivm ) { + Com_Error( ERR_FATAL, "VM_Create on UI failed" ); + } + + // sanity check + v = VM_Call( uivm, UI_GETAPIVERSION ); + if (v == UI_OLD_API_VERSION) { +// Com_Printf(S_COLOR_YELLOW "WARNING: loading old Quake III Arena User Interface version %d\n", v ); + // init for this gamestate + VM_Call( uivm, UI_INIT, (cls.state >= CA_AUTHORIZING && cls.state < CA_ACTIVE)); + } + else if (v != UI_API_VERSION) { + Com_Error( ERR_DROP, "User Interface is version %d, expected %d", v, UI_API_VERSION ); + cls.uiStarted = qfalse; + } + else { + // init for this gamestate + VM_Call( uivm, UI_INIT, (cls.state >= CA_AUTHORIZING && cls.state < CA_ACTIVE) ); + } + + // reset any CVAR_CHEAT cvars registered by ui + if ( !clc.demoplaying && !cl_connectedToCheatServer ) + Cvar_SetCheatState(); +} + +#ifndef STANDALONE +qboolean UI_usesUniqueCDKey( void ) { + if (uivm) { + return (VM_Call( uivm, UI_HASUNIQUECDKEY) == qtrue); + } else { + return qfalse; + } +} +#endif + +/* +==================== +UI_GameCommand + +See if the current console command is claimed by the ui +==================== +*/ +qboolean UI_GameCommand( void ) { + if ( !uivm ) { + return qfalse; + } + + return VM_Call( uivm, UI_CONSOLE_COMMAND, cls.realtime ); +} diff --git a/reaction/engine/code/client/client.h b/reaction/engine/code/client/client.h new file mode 100644 index 00000000..5ee30c29 --- /dev/null +++ b/reaction/engine/code/client/client.h @@ -0,0 +1,625 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// client.h -- primary header for client + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "../renderer/tr_public.h" +#include "../ui/ui_public.h" +#include "keys.h" +#include "snd_public.h" +#include "../cgame/cg_public.h" +#include "../game/bg_public.h" + +#ifdef USE_CURL +#include "cl_curl.h" +#endif /* USE_CURL */ + +#ifdef USE_VOIP +#include "speex/speex.h" +#include "speex/speex_preprocess.h" +#endif + +// file full of random crap that gets used to create cl_guid +#define QKEY_FILE "qkey" +#define QKEY_SIZE 2048 + +#define RETRANSMIT_TIMEOUT 3000 // time between connection packet retransmits + +// snapshots are a view of the server at a given time +typedef struct { + qboolean valid; // cleared if delta parsing was invalid + int snapFlags; // rate delayed and dropped commands + + int serverTime; // server time the message is valid for (in msec) + + int messageNum; // copied from netchan->incoming_sequence + int deltaNum; // messageNum the delta is from + int ping; // time from when cmdNum-1 was sent to time packet was reeceived + byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits + + int cmdNum; // the next cmdNum the server is expecting + playerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + int parseEntitiesNum; // at the time of this snapshot + + int serverCommandNum; // execute all commands up to this before + // making the snapshot current +} clSnapshot_t; + + + +/* +============================================================================= + +the clientActive_t structure is wiped completely at every +new gamestate_t, potentially several times during an established connection + +============================================================================= +*/ + +typedef struct { + int p_cmdNumber; // cl.cmdNumber when packet was sent + int p_serverTime; // usercmd->serverTime when packet was sent + int p_realtime; // cls.realtime when packet was sent +} outPacket_t; + +// the parseEntities array must be large enough to hold PACKET_BACKUP frames of +// entities, so that when a delta compressed message arives from the server +// it can be un-deltad from the original +#define MAX_PARSE_ENTITIES 2048 + +extern int g_console_field_width; + +typedef struct { + int timeoutcount; // it requres several frames in a timeout condition + // to disconnect, preventing debugging breaks from + // causing immediate disconnects on continue + clSnapshot_t snap; // latest received from server + + int serverTime; // may be paused during play + int oldServerTime; // to prevent time from flowing bakcwards + int oldFrameServerTime; // to check tournament restarts + int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta + // this value changes as net lag varies + qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate + // cleared when CL_AdjustTimeDelta looks at it + qboolean newSnapshots; // set on parse of any valid packet + + gameState_t gameState; // configstrings + char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO + + int parseEntitiesNum; // index (not anded off) into cl_parse_entities[] + + int mouseDx[2], mouseDy[2]; // added to by mouse events + int mouseIndex; + int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events + + // cgame communicates a few values to the client system + int cgameUserCmdValue; // current weapon to add to usercmd_t + float cgameSensitivity; + + // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last + // properly generated command + usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds + int cmdNumber; // incremented each frame, because multiple + // frames may need to be packed into a single packet + + outPacket_t outPackets[PACKET_BACKUP]; // information about each packet we have sent out + + // the client maintains its own idea of view angles, which are + // sent to the server each frame. It is cleared to 0 upon entering each level. + // the server sends a delta each frame which is added to the locally + // tracked view angles to account for standing on rotating objects, + // and teleport direction changes + vec3_t viewangles; + + int serverId; // included in each client message so the server + // can tell if it is for a prior map_restart + // big stuff at end of structure so most offsets are 15 bits or less + clSnapshot_t snapshots[PACKET_BACKUP]; + + entityState_t entityBaselines[MAX_GENTITIES]; // for delta compression when not in previous frame + + entityState_t parseEntities[MAX_PARSE_ENTITIES]; +} clientActive_t; + +extern clientActive_t cl; + +/* +============================================================================= + +the clientConnection_t structure is wiped when disconnecting from a server, +either to go to a full screen console, play a demo, or connect to a different server + +A connection can be to either a server through the network layer or a +demo through a file. + +============================================================================= +*/ + +#define MAX_TIMEDEMO_DURATIONS 4096 + +typedef struct { + + int clientNum; + int lastPacketSentTime; // for retransmits during connection + int lastPacketTime; // for timeouts + + netadr_t serverAddress; + int connectTime; // for connection retransmits + int connectPacketCount; // for display on connection dialog + char serverMessage[MAX_STRING_TOKENS]; // for display on connection dialog + + int challenge; // from the server to use for connecting + int checksumFeed; // from the server for checksum calculations + + // these are our reliable messages that go to the server + int reliableSequence; + int reliableAcknowledge; // the last one the server has executed + char reliableCommands[MAX_RELIABLE_COMMANDS][MAX_STRING_CHARS]; + + // server message (unreliable) and command (reliable) sequence + // numbers are NOT cleared at level changes, but continue to + // increase as long as the connection is valid + + // message sequence is used by both the network layer and the + // delta compression layer + int serverMessageSequence; + + // reliable messages received from server + int serverCommandSequence; + int lastExecutedServerCommand; // last server command grabbed or executed with CL_GetServerCommand + char serverCommands[MAX_RELIABLE_COMMANDS][MAX_STRING_CHARS]; + + // file transfer from server + fileHandle_t download; + char downloadTempName[MAX_OSPATH]; + char downloadName[MAX_OSPATH]; +#ifdef USE_CURL + qboolean cURLEnabled; + qboolean cURLUsed; + qboolean cURLDisconnected; + char downloadURL[MAX_OSPATH]; + CURL *downloadCURL; + CURLM *downloadCURLM; +#endif /* USE_CURL */ + int sv_allowDownload; + char sv_dlURL[MAX_CVAR_VALUE_STRING]; + int downloadNumber; + int downloadBlock; // block we are waiting for + int downloadCount; // how many bytes we got + int downloadSize; // how many bytes we got + char downloadList[MAX_INFO_STRING]; // list of paks we need to download + qboolean downloadRestart; // if true, we need to do another FS_Restart because we downloaded a pak + + // demo information + char demoName[MAX_QPATH]; + qboolean spDemoRecording; + qboolean demorecording; + qboolean demoplaying; + qboolean demowaiting; // don't record until a non-delta message is received + qboolean firstDemoFrameSkipped; + fileHandle_t demofile; + + int timeDemoFrames; // counter of rendered frames + int timeDemoStart; // cls.realtime before first frame + int timeDemoBaseTime; // each frame will be at this time + frameNum * 50 + int timeDemoLastFrame;// time the last frame was rendered + int timeDemoMinDuration; // minimum frame duration + int timeDemoMaxDuration; // maximum frame duration + unsigned char timeDemoDurations[ MAX_TIMEDEMO_DURATIONS ]; // log of frame durations + +#ifdef USE_VOIP + qboolean speexInitialized; + int speexFrameSize; + int speexSampleRate; + + // incoming data... + // !!! FIXME: convert from parallel arrays to array of a struct. + SpeexBits speexDecoderBits[MAX_CLIENTS]; + void *speexDecoder[MAX_CLIENTS]; + byte voipIncomingGeneration[MAX_CLIENTS]; + int voipIncomingSequence[MAX_CLIENTS]; + float voipGain[MAX_CLIENTS]; + qboolean voipIgnore[MAX_CLIENTS]; + qboolean voipMuteAll; + + // outgoing data... + int voipTarget1; // these three ints make up a bit mask of 92 bits. + int voipTarget2; // the bits say who a VoIP pack is addressed to: + int voipTarget3; // (1 << clientnum). See cl_voipSendTarget cvar. + SpeexPreprocessState *speexPreprocessor; + SpeexBits speexEncoderBits; + void *speexEncoder; + int voipOutgoingDataSize; + int voipOutgoingDataFrames; + int voipOutgoingSequence; + byte voipOutgoingGeneration; + byte voipOutgoingData[1024]; + float voipPower; +#endif + + // big stuff at end of structure so most offsets are 15 bits or less + netchan_t netchan; +} clientConnection_t; + +extern clientConnection_t clc; + +/* +================================================================== + +the clientStatic_t structure is never wiped, and is used even when +no client connection is active at all + +================================================================== +*/ + +typedef struct { + netadr_t adr; + int start; + int time; + char info[MAX_INFO_STRING]; +} ping_t; + +typedef struct { + netadr_t adr; + char hostName[MAX_NAME_LENGTH]; + char mapName[MAX_NAME_LENGTH]; + char game[MAX_NAME_LENGTH]; + int netType; + int gameType; + int clients; + int maxClients; + int minPing; + int maxPing; + int ping; + qboolean visible; + int punkbuster; +} serverInfo_t; + +typedef struct { + connstate_t state; // connection status + + qboolean cddialog; // bring up the cd needed dialog next frame + + char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect) + + // when the server clears the hunk, all of these must be restarted + qboolean rendererStarted; + qboolean soundStarted; + qboolean soundRegistered; + qboolean uiStarted; + qboolean cgameStarted; + + int framecount; + int frametime; // msec since last frame + + int realtime; // ignores pause + int realFrametime; // ignoring pause, so console always works + + int numlocalservers; + serverInfo_t localServers[MAX_OTHER_SERVERS]; + + int numglobalservers; + serverInfo_t globalServers[MAX_GLOBAL_SERVERS]; + // additional global servers + int numGlobalServerAddresses; + netadr_t globalServerAddresses[MAX_GLOBAL_SERVERS]; + + int numfavoriteservers; + serverInfo_t favoriteServers[MAX_OTHER_SERVERS]; + + int pingUpdateSource; // source currently pinging or updating + + // update server info + netadr_t updateServer; + char updateChallenge[MAX_TOKEN_CHARS]; + char updateInfoString[MAX_INFO_STRING]; + + netadr_t authorizeServer; + + // rendering info + glconfig_t glconfig; + qhandle_t charSetShader; + qhandle_t whiteShader; + qhandle_t consoleShader; +} clientStatic_t; + +extern clientStatic_t cls; + +//============================================================================= + +extern vm_t *cgvm; // interface to cgame dll or vm +extern vm_t *uivm; // interface to ui dll or vm +extern refexport_t re; // interface to refresh .dll + + +// +// cvars +// +extern cvar_t *cl_nodelta; +extern cvar_t *cl_debugMove; +extern cvar_t *cl_noprint; +extern cvar_t *cl_timegraph; +extern cvar_t *cl_maxpackets; +extern cvar_t *cl_packetdup; +extern cvar_t *cl_shownet; +extern cvar_t *cl_showSend; +extern cvar_t *cl_timeNudge; +extern cvar_t *cl_showTimeDelta; +extern cvar_t *cl_freezeDemo; + +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_run; +extern cvar_t *cl_anglespeedkey; + +extern cvar_t *cl_sensitivity; +extern cvar_t *cl_freelook; + +extern cvar_t *cl_mouseAccel; +extern cvar_t *cl_showMouseRate; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; +extern cvar_t *m_filter; + +extern cvar_t *cl_timedemo; +extern cvar_t *cl_aviFrameRate; +extern cvar_t *cl_aviMotionJpeg; + +extern cvar_t *cl_activeAction; + +extern cvar_t *cl_allowDownload; +extern cvar_t *cl_downloadMethod; +extern cvar_t *cl_conXOffset; +extern cvar_t *cl_inGameVideo; + +extern cvar_t *cl_lanForcePackets; +extern cvar_t *cl_autoRecordDemo; + +extern cvar_t *cl_consoleKeys; + +#ifdef USE_MUMBLE +extern cvar_t *cl_useMumble; +extern cvar_t *cl_mumbleScale; +#endif + +#ifdef USE_VOIP +// cl_voipSendTarget is a string: "all" to broadcast to everyone, "none" to +// send to no one, or a comma-separated list of client numbers: +// "0,7,2,23" ... an empty string is treated like "all". +extern cvar_t *cl_voipUseVAD; +extern cvar_t *cl_voipVADThreshold; +extern cvar_t *cl_voipSend; +extern cvar_t *cl_voipSendTarget; +extern cvar_t *cl_voipGainDuringCapture; +extern cvar_t *cl_voipCaptureMult; +extern cvar_t *cl_voipShowMeter; +extern cvar_t *cl_voip; +#endif + +//================================================= + +// +// cl_main +// + +void CL_Init (void); +void CL_FlushMemory(void); +void CL_ShutdownAll(void); +void CL_AddReliableCommand( const char *cmd ); + +void CL_StartHunkUsers( qboolean rendererOnly ); + +void CL_Disconnect_f (void); +void CL_GetChallengePacket (void); +void CL_Vid_Restart_f( void ); +void CL_Snd_Restart_f (void); +void CL_StartDemoLoop( void ); +void CL_NextDemo( void ); +void CL_ReadDemoMessage( void ); +void CL_StopRecord_f(void); + +void CL_InitDownloads(void); +void CL_NextDownload(void); + +void CL_GetPing( int n, char *buf, int buflen, int *pingtime ); +void CL_GetPingInfo( int n, char *buf, int buflen ); +void CL_ClearPing( int n ); +int CL_GetPingQueueCount( void ); + +void CL_ShutdownRef( void ); +void CL_InitRef( void ); +#ifndef STANDALONE +qboolean CL_CDKeyValidate( const char *key, const char *checksum ); +#endif +int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen ); + +qboolean CL_CheckPaused(void); + +// +// cl_input +// +typedef struct { + int down[2]; // key nums holding it down + unsigned downtime; // msec timestamp + unsigned msec; // msec down this frame if both a down and up happened + qboolean active; // current state + qboolean wasPressed; // set when down, not cleared when up +} kbutton_t; + +extern kbutton_t in_mlook, in_klook; +extern kbutton_t in_strafe; +extern kbutton_t in_speed; + +#ifdef USE_VOIP +extern kbutton_t in_voiprecord; +#endif + +void CL_InitInput (void); +void CL_SendCmd (void); +void CL_ClearState (void); +void CL_ReadPackets (void); + +void CL_WritePacket( void ); +void IN_CenterView (void); + +void CL_VerifyCode( void ); + +float CL_KeyState (kbutton_t *key); +int Key_StringToKeynum( char *str ); +char *Key_KeynumToString (int keynum); + +// +// cl_parse.c +// +extern int cl_connectedToPureServer; +extern int cl_connectedToCheatServer; + +#ifdef USE_VOIP +extern int cl_connectedToVoipServer; +void CL_Voip_f( void ); +#endif + +void CL_SystemInfoChanged( void ); +void CL_ParseServerMessage( msg_t *msg ); + +//==================================================================== + +void CL_ServerInfoPacket( netadr_t from, msg_t *msg ); +void CL_LocalServers_f( void ); +void CL_GlobalServers_f( void ); +void CL_FavoriteServers_f( void ); +void CL_Ping_f( void ); +qboolean CL_UpdateVisiblePings_f( int source ); + + +// +// console +// +void Con_DrawCharacter (int cx, int line, int num); + +void Con_CheckResize (void); +void Con_Init (void); +void Con_Clear_f (void); +void Con_ToggleConsole_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_RunConsole (void); +void Con_DrawConsole (void); +void Con_PageUp( void ); +void Con_PageDown( void ); +void Con_Top( void ); +void Con_Bottom( void ); +void Con_Close( void ); + +void CL_LoadConsoleHistory( void ); +void CL_SaveConsoleHistory( void ); + +// +// cl_scrn.c +// +void SCR_Init (void); +void SCR_UpdateScreen (void); + +void SCR_DebugGraph (float value, int color); + +int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates + +void SCR_AdjustFrom640( float *x, float *y, float *w, float *h ); +void SCR_FillRect( float x, float y, float width, float height, + const float *color ); +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ); + +void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ); // draws a string with embedded color control characters with fade +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ); // ignores embedded color control characters +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor, qboolean noColorEscape ); +void SCR_DrawSmallChar( int x, int y, int ch ); + + +// +// cl_cin.c +// + +void CL_PlayCinematic_f( void ); +void SCR_DrawCinematic (void); +void SCR_RunCinematic (void); +void SCR_StopCinematic (void); +int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits); +e_status CIN_StopCinematic(int handle); +e_status CIN_RunCinematic (int handle); +void CIN_DrawCinematic (int handle); +void CIN_SetExtents (int handle, int x, int y, int w, int h); +void CIN_SetLooping (int handle, qboolean loop); +void CIN_UploadCinematic(int handle); +void CIN_CloseAllVideos(void); + +// +// cl_cgame.c +// +void CL_InitCGame( void ); +void CL_ShutdownCGame( void ); +qboolean CL_GameCommand( void ); +void CL_CGameRendering( stereoFrame_t stereo ); +void CL_SetCGameTime( void ); +void CL_FirstSnapshot( void ); +void CL_ShaderStateChanged(void); + +// +// cl_ui.c +// +void CL_InitUI( void ); +void CL_ShutdownUI( void ); +int Key_GetCatcher( void ); +void Key_SetCatcher( int catcher ); +void LAN_LoadCachedServers( void ); +void LAN_SaveServersToCache( void ); + + +// +// cl_net_chan.c +// +void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg); //int length, const byte *data ); +void CL_Netchan_TransmitNextFragment( netchan_t *chan ); +qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ); + +// +// cl_avi.c +// +qboolean CL_OpenAVIForWriting( const char *filename ); +void CL_TakeVideoFrame( void ); +void CL_WriteAVIVideoFrame( const byte *imageBuffer, int size ); +void CL_WriteAVIAudioFrame( const byte *pcmBuffer, int size ); +qboolean CL_CloseAVI( void ); +qboolean CL_VideoRecording( void ); + +// +// cl_main.c +// +void CL_WriteDemoMessage ( msg_t *msg, int headerBytes ); + diff --git a/reaction/engine/code/client/keycodes.h b/reaction/engine/code/client/keycodes.h new file mode 100644 index 00000000..706ca1b0 --- /dev/null +++ b/reaction/engine/code/client/keycodes.h @@ -0,0 +1,279 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +#ifndef __KEYCODES_H__ +#define __KEYCODES_H__ + +// +// these are the key numbers that should be passed to KeyEvent +// + +// normal keys should be passed as lowercased ascii + +typedef enum { + K_TAB = 9, + K_ENTER = 13, + K_ESCAPE = 27, + K_SPACE = 32, + + K_BACKSPACE = 127, + + K_COMMAND = 128, + K_CAPSLOCK, + K_POWER, + K_PAUSE, + + K_UPARROW, + K_DOWNARROW, + K_LEFTARROW, + K_RIGHTARROW, + + K_ALT, + K_CTRL, + K_SHIFT, + K_INS, + K_DEL, + K_PGDN, + K_PGUP, + K_HOME, + K_END, + + K_F1, + K_F2, + K_F3, + K_F4, + K_F5, + K_F6, + K_F7, + K_F8, + K_F9, + K_F10, + K_F11, + K_F12, + K_F13, + K_F14, + K_F15, + + K_KP_HOME, + K_KP_UPARROW, + K_KP_PGUP, + K_KP_LEFTARROW, + K_KP_5, + K_KP_RIGHTARROW, + K_KP_END, + K_KP_DOWNARROW, + K_KP_PGDN, + K_KP_ENTER, + K_KP_INS, + K_KP_DEL, + K_KP_SLASH, + K_KP_MINUS, + K_KP_PLUS, + K_KP_NUMLOCK, + K_KP_STAR, + K_KP_EQUALS, + + K_MOUSE1, + K_MOUSE2, + K_MOUSE3, + K_MOUSE4, + K_MOUSE5, + + K_MWHEELDOWN, + K_MWHEELUP, + + K_JOY1, + K_JOY2, + K_JOY3, + K_JOY4, + K_JOY5, + K_JOY6, + K_JOY7, + K_JOY8, + K_JOY9, + K_JOY10, + K_JOY11, + K_JOY12, + K_JOY13, + K_JOY14, + K_JOY15, + K_JOY16, + K_JOY17, + K_JOY18, + K_JOY19, + K_JOY20, + K_JOY21, + K_JOY22, + K_JOY23, + K_JOY24, + K_JOY25, + K_JOY26, + K_JOY27, + K_JOY28, + K_JOY29, + K_JOY30, + K_JOY31, + K_JOY32, + + K_AUX1, + K_AUX2, + K_AUX3, + K_AUX4, + K_AUX5, + K_AUX6, + K_AUX7, + K_AUX8, + K_AUX9, + K_AUX10, + K_AUX11, + K_AUX12, + K_AUX13, + K_AUX14, + K_AUX15, + K_AUX16, + + K_WORLD_0, + K_WORLD_1, + K_WORLD_2, + K_WORLD_3, + K_WORLD_4, + K_WORLD_5, + K_WORLD_6, + K_WORLD_7, + K_WORLD_8, + K_WORLD_9, + K_WORLD_10, + K_WORLD_11, + K_WORLD_12, + K_WORLD_13, + K_WORLD_14, + K_WORLD_15, + K_WORLD_16, + K_WORLD_17, + K_WORLD_18, + K_WORLD_19, + K_WORLD_20, + K_WORLD_21, + K_WORLD_22, + K_WORLD_23, + K_WORLD_24, + K_WORLD_25, + K_WORLD_26, + K_WORLD_27, + K_WORLD_28, + K_WORLD_29, + K_WORLD_30, + K_WORLD_31, + K_WORLD_32, + K_WORLD_33, + K_WORLD_34, + K_WORLD_35, + K_WORLD_36, + K_WORLD_37, + K_WORLD_38, + K_WORLD_39, + K_WORLD_40, + K_WORLD_41, + K_WORLD_42, + K_WORLD_43, + K_WORLD_44, + K_WORLD_45, + K_WORLD_46, + K_WORLD_47, + K_WORLD_48, + K_WORLD_49, + K_WORLD_50, + K_WORLD_51, + K_WORLD_52, + K_WORLD_53, + K_WORLD_54, + K_WORLD_55, + K_WORLD_56, + K_WORLD_57, + K_WORLD_58, + K_WORLD_59, + K_WORLD_60, + K_WORLD_61, + K_WORLD_62, + K_WORLD_63, + K_WORLD_64, + K_WORLD_65, + K_WORLD_66, + K_WORLD_67, + K_WORLD_68, + K_WORLD_69, + K_WORLD_70, + K_WORLD_71, + K_WORLD_72, + K_WORLD_73, + K_WORLD_74, + K_WORLD_75, + K_WORLD_76, + K_WORLD_77, + K_WORLD_78, + K_WORLD_79, + K_WORLD_80, + K_WORLD_81, + K_WORLD_82, + K_WORLD_83, + K_WORLD_84, + K_WORLD_85, + K_WORLD_86, + K_WORLD_87, + K_WORLD_88, + K_WORLD_89, + K_WORLD_90, + K_WORLD_91, + K_WORLD_92, + K_WORLD_93, + K_WORLD_94, + K_WORLD_95, + + K_SUPER, + K_COMPOSE, + K_MODE, + K_HELP, + K_PRINT, + K_SYSREQ, + K_SCROLLOCK, + K_BREAK, + K_MENU, + K_EURO, + K_UNDO, + + // Pseudo-key that brings the console down + K_CONSOLE, + + MAX_KEYS +} keyNum_t; + +// MAX_KEYS replaces K_LAST_KEY, however some mods may have used K_LAST_KEY +// in detecting binds, so we leave it defined to the old hardcoded value +// of maxiumum keys to prevent mods from crashing older versions of the engine +#define K_LAST_KEY 256 + +// The menu code needs to get both key and char events, but +// to avoid duplicating the paths, the char events are just +// distinguished by or'ing in K_CHAR_FLAG (ugly) +#define K_CHAR_FLAG 1024 + +#endif diff --git a/reaction/engine/code/client/keys.h b/reaction/engine/code/client/keys.h new file mode 100644 index 00000000..01684684 --- /dev/null +++ b/reaction/engine/code/client/keys.h @@ -0,0 +1,55 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#include "keycodes.h" + +typedef struct { + qboolean down; + int repeats; // if > 1, it is autorepeating + char *binding; +} qkey_t; + +extern qboolean key_overstrikeMode; +extern qkey_t keys[MAX_KEYS]; + +// NOTE TTimo the declaration of field_t and Field_Clear is now in qcommon/qcommon.h +void Field_KeyDownEvent( field_t *edit, int key ); +void Field_CharEvent( field_t *edit, int ch ); +void Field_Draw( field_t *edit, int x, int y, int width, qboolean showCursor, qboolean noColorEscape ); +void Field_BigDraw( field_t *edit, int x, int y, int width, qboolean showCursor, qboolean noColorEscape ); + +#define COMMAND_HISTORY 32 +extern field_t historyEditLines[COMMAND_HISTORY]; + +extern field_t g_consoleField; +extern field_t chatField; +extern int anykeydown; +extern qboolean chat_team; +extern int chat_playerNum; + +void Key_WriteBindings( fileHandle_t f ); +void Key_SetBinding( int keynum, const char *binding ); +char *Key_GetBinding( int keynum ); +qboolean Key_IsDown( int keynum ); +qboolean Key_GetOverstrikeMode( void ); +void Key_SetOverstrikeMode( qboolean state ); +void Key_ClearStates( void ); +int Key_GetKey(const char *binding); diff --git a/reaction/engine/code/client/libmumblelink.c b/reaction/engine/code/client/libmumblelink.c new file mode 100644 index 00000000..c45e6a1d --- /dev/null +++ b/reaction/engine/code/client/libmumblelink.c @@ -0,0 +1,134 @@ +/* libmumblelink.c -- mumble link interface + + Copyright (C) 2008 Ludwig Nussel + + 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. + +*/ + +#ifdef WIN32 +#include +#define uint32_t UINT32 +#else +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include "libmumblelink.h" + +typedef struct +{ + uint32_t uiVersion; + uint32_t uiTick; + float fPosition[3]; + float fFront[3]; + float fTop[3]; + wchar_t name[256]; +} LinkedMem; + +static LinkedMem *lm = NULL; + +#ifdef WIN32 +static HANDLE hMapObject = NULL; +#else +static int32_t GetTickCount(void) +{ + struct timeval tv; + gettimeofday(&tv,NULL); + + return tv.tv_usec / 1000 + tv.tv_sec * 1000; +} +#endif + +int mumble_link(const char* name) +{ +#ifdef WIN32 + if(lm) + return 0; + + hMapObject = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, L"MumbleLink"); + if (hMapObject == NULL) + return -1; + + lm = (LinkedMem *) MapViewOfFile(hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(LinkedMem)); + if (lm == NULL) { + CloseHandle(hMapObject); + hMapObject = NULL; + return -1; + } +#else + char file[256]; + int shmfd; + if(lm) + return 0; + + snprintf(file, sizeof (file), "/MumbleLink.%d", getuid()); + shmfd = shm_open(file, O_RDWR, S_IRUSR | S_IWUSR); + if(shmfd < 0) { + return -1; + } + + lm = (LinkedMem *) (mmap(NULL, sizeof(LinkedMem), PROT_READ | PROT_WRITE, MAP_SHARED, shmfd,0)); + if (lm == (void *) (-1)) { + lm = NULL; + } + close(shmfd); +#endif + mbstowcs(lm->name, name, sizeof(lm->name)); + + return 0; +} + +void mumble_update_coordinates(float fPosition[3], float fFront[3], float fTop[3]) +{ + if (!lm) + return; + + memcpy(lm->fPosition, fPosition, sizeof(fPosition)); + memcpy(lm->fFront, fFront, sizeof(fFront)); + memcpy(lm->fTop, fTop, sizeof(fTop)); + lm->uiVersion = 1; + lm->uiTick = GetTickCount(); +} + +void mumble_unlink() +{ + if(!lm) + return; +#ifdef WIN32 + UnmapViewOfFile(lm); + CloseHandle(hMapObject); + hMapObject = NULL; +#else + munmap(lm, sizeof(LinkedMem)); +#endif + lm = NULL; +} + +int mumble_islinked(void) +{ + return lm != NULL; +} diff --git a/reaction/engine/code/client/libmumblelink.h b/reaction/engine/code/client/libmumblelink.h new file mode 100644 index 00000000..0623931e --- /dev/null +++ b/reaction/engine/code/client/libmumblelink.h @@ -0,0 +1,26 @@ +/* libmumblelink.h -- mumble link interface + + Copyright (C) 2008 Ludwig Nussel + + 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. + +*/ + +int mumble_link(const char* name); +int mumble_islinked(void); +void mumble_update_coordinates(float fPosition[3], float fFront[3], float fTop[3]); +void mumble_unlink(void); diff --git a/reaction/engine/code/client/qal.c b/reaction/engine/code/client/qal.c new file mode 100644 index 00000000..5ddd67f2 --- /dev/null +++ b/reaction/engine/code/client/qal.c @@ -0,0 +1,351 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com) + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +// Dynamically loads OpenAL + +#ifdef USE_OPENAL + +#include "qal.h" + +#ifdef USE_OPENAL_DLOPEN + +#include "../sys/sys_loadlib.h" + +LPALENABLE qalEnable; +LPALDISABLE qalDisable; +LPALISENABLED qalIsEnabled; +LPALGETSTRING qalGetString; +LPALGETBOOLEANV qalGetBooleanv; +LPALGETINTEGERV qalGetIntegerv; +LPALGETFLOATV qalGetFloatv; +LPALGETDOUBLEV qalGetDoublev; +LPALGETBOOLEAN qalGetBoolean; +LPALGETINTEGER qalGetInteger; +LPALGETFLOAT qalGetFloat; +LPALGETDOUBLE qalGetDouble; +LPALGETERROR qalGetError; +LPALISEXTENSIONPRESENT qalIsExtensionPresent; +LPALGETPROCADDRESS qalGetProcAddress; +LPALGETENUMVALUE qalGetEnumValue; +LPALLISTENERF qalListenerf; +LPALLISTENER3F qalListener3f; +LPALLISTENERFV qalListenerfv; +LPALLISTENERI qalListeneri; +LPALGETLISTENERF qalGetListenerf; +LPALGETLISTENER3F qalGetListener3f; +LPALGETLISTENERFV qalGetListenerfv; +LPALGETLISTENERI qalGetListeneri; +LPALGENSOURCES qalGenSources; +LPALDELETESOURCES qalDeleteSources; +LPALISSOURCE qalIsSource; +LPALSOURCEF qalSourcef; +LPALSOURCE3F qalSource3f; +LPALSOURCEFV qalSourcefv; +LPALSOURCEI qalSourcei; +LPALGETSOURCEF qalGetSourcef; +LPALGETSOURCE3F qalGetSource3f; +LPALGETSOURCEFV qalGetSourcefv; +LPALGETSOURCEI qalGetSourcei; +LPALSOURCEPLAYV qalSourcePlayv; +LPALSOURCESTOPV qalSourceStopv; +LPALSOURCEREWINDV qalSourceRewindv; +LPALSOURCEPAUSEV qalSourcePausev; +LPALSOURCEPLAY qalSourcePlay; +LPALSOURCESTOP qalSourceStop; +LPALSOURCEREWIND qalSourceRewind; +LPALSOURCEPAUSE qalSourcePause; +LPALSOURCEQUEUEBUFFERS qalSourceQueueBuffers; +LPALSOURCEUNQUEUEBUFFERS qalSourceUnqueueBuffers; +LPALGENBUFFERS qalGenBuffers; +LPALDELETEBUFFERS qalDeleteBuffers; +LPALISBUFFER qalIsBuffer; +LPALBUFFERDATA qalBufferData; +LPALGETBUFFERF qalGetBufferf; +LPALGETBUFFERI qalGetBufferi; +LPALDOPPLERFACTOR qalDopplerFactor; +LPALDOPPLERVELOCITY qalDopplerVelocity; +LPALDISTANCEMODEL qalDistanceModel; + +LPALCCREATECONTEXT qalcCreateContext; +LPALCMAKECONTEXTCURRENT qalcMakeContextCurrent; +LPALCPROCESSCONTEXT qalcProcessContext; +LPALCSUSPENDCONTEXT qalcSuspendContext; +LPALCDESTROYCONTEXT qalcDestroyContext; +LPALCGETCURRENTCONTEXT qalcGetCurrentContext; +LPALCGETCONTEXTSDEVICE qalcGetContextsDevice; +LPALCOPENDEVICE qalcOpenDevice; +LPALCCLOSEDEVICE qalcCloseDevice; +LPALCGETERROR qalcGetError; +LPALCISEXTENSIONPRESENT qalcIsExtensionPresent; +LPALCGETPROCADDRESS qalcGetProcAddress; +LPALCGETENUMVALUE qalcGetEnumValue; +LPALCGETSTRING qalcGetString; +LPALCGETINTEGERV qalcGetIntegerv; +LPALCCAPTUREOPENDEVICE qalcCaptureOpenDevice; +LPALCCAPTURECLOSEDEVICE qalcCaptureCloseDevice; +LPALCCAPTURESTART qalcCaptureStart; +LPALCCAPTURESTOP qalcCaptureStop; +LPALCCAPTURESAMPLES qalcCaptureSamples; + +static void *OpenALLib = NULL; + +static qboolean alinit_fail = qfalse; + +/* +================= +GPA +================= +*/ +static void *GPA(char *str) +{ + void *rv; + + rv = Sys_LoadFunction(OpenALLib, str); + if(!rv) + { + Com_Printf( " Can't load symbol %s\n", str); + alinit_fail = qtrue; + return NULL; + } + else + { + Com_DPrintf( " Loaded symbol %s (%p)\n", str, rv); + return rv; + } +} + +/* +================= +QAL_Init +================= +*/ +qboolean QAL_Init(const char *libname) +{ + if(OpenALLib) + return qtrue; + + Com_Printf( "Loading \"%s\"...\n", libname); + if( (OpenALLib = Sys_LoadLibrary(libname)) == 0 ) + { +#ifdef _WIN32 + return qfalse; +#else + char fn[1024]; + Q_strncpyz( fn, Sys_Cwd( ), sizeof( fn ) ); + strncat(fn, "/", sizeof(fn) - strlen(fn) - 1); + strncat(fn, libname, sizeof(fn) - strlen(fn) - 1); + + if( (OpenALLib = Sys_LoadLibrary(fn)) == 0 ) + { + return qfalse; + } +#endif + } + + alinit_fail = qfalse; + + qalEnable = GPA("alEnable"); + qalDisable = GPA("alDisable"); + qalIsEnabled = GPA("alIsEnabled"); + qalGetString = GPA("alGetString"); + qalGetBooleanv = GPA("alGetBooleanv"); + qalGetIntegerv = GPA("alGetIntegerv"); + qalGetFloatv = GPA("alGetFloatv"); + qalGetDoublev = GPA("alGetDoublev"); + qalGetBoolean = GPA("alGetBoolean"); + qalGetInteger = GPA("alGetInteger"); + qalGetFloat = GPA("alGetFloat"); + qalGetDouble = GPA("alGetDouble"); + qalGetError = GPA("alGetError"); + qalIsExtensionPresent = GPA("alIsExtensionPresent"); + qalGetProcAddress = GPA("alGetProcAddress"); + qalGetEnumValue = GPA("alGetEnumValue"); + qalListenerf = GPA("alListenerf"); + qalListener3f = GPA("alListener3f"); + qalListenerfv = GPA("alListenerfv"); + qalListeneri = GPA("alListeneri"); + qalGetListenerf = GPA("alGetListenerf"); + qalGetListener3f = GPA("alGetListener3f"); + qalGetListenerfv = GPA("alGetListenerfv"); + qalGetListeneri = GPA("alGetListeneri"); + qalGenSources = GPA("alGenSources"); + qalDeleteSources = GPA("alDeleteSources"); + qalIsSource = GPA("alIsSource"); + qalSourcef = GPA("alSourcef"); + qalSource3f = GPA("alSource3f"); + qalSourcefv = GPA("alSourcefv"); + qalSourcei = GPA("alSourcei"); + qalGetSourcef = GPA("alGetSourcef"); + qalGetSource3f = GPA("alGetSource3f"); + qalGetSourcefv = GPA("alGetSourcefv"); + qalGetSourcei = GPA("alGetSourcei"); + qalSourcePlayv = GPA("alSourcePlayv"); + qalSourceStopv = GPA("alSourceStopv"); + qalSourceRewindv = GPA("alSourceRewindv"); + qalSourcePausev = GPA("alSourcePausev"); + qalSourcePlay = GPA("alSourcePlay"); + qalSourceStop = GPA("alSourceStop"); + qalSourceRewind = GPA("alSourceRewind"); + qalSourcePause = GPA("alSourcePause"); + qalSourceQueueBuffers = GPA("alSourceQueueBuffers"); + qalSourceUnqueueBuffers = GPA("alSourceUnqueueBuffers"); + qalGenBuffers = GPA("alGenBuffers"); + qalDeleteBuffers = GPA("alDeleteBuffers"); + qalIsBuffer = GPA("alIsBuffer"); + qalBufferData = GPA("alBufferData"); + qalGetBufferf = GPA("alGetBufferf"); + qalGetBufferi = GPA("alGetBufferi"); + qalDopplerFactor = GPA("alDopplerFactor"); + qalDopplerVelocity = GPA("alDopplerVelocity"); + qalDistanceModel = GPA("alDistanceModel"); + + qalcCreateContext = GPA("alcCreateContext"); + qalcMakeContextCurrent = GPA("alcMakeContextCurrent"); + qalcProcessContext = GPA("alcProcessContext"); + qalcSuspendContext = GPA("alcSuspendContext"); + qalcDestroyContext = GPA("alcDestroyContext"); + qalcGetCurrentContext = GPA("alcGetCurrentContext"); + qalcGetContextsDevice = GPA("alcGetContextsDevice"); + qalcOpenDevice = GPA("alcOpenDevice"); + qalcCloseDevice = GPA("alcCloseDevice"); + qalcGetError = GPA("alcGetError"); + qalcIsExtensionPresent = GPA("alcIsExtensionPresent"); + qalcGetProcAddress = GPA("alcGetProcAddress"); + qalcGetEnumValue = GPA("alcGetEnumValue"); + qalcGetString = GPA("alcGetString"); + qalcGetIntegerv = GPA("alcGetIntegerv"); + qalcCaptureOpenDevice = GPA("alcCaptureOpenDevice"); + qalcCaptureCloseDevice = GPA("alcCaptureCloseDevice"); + qalcCaptureStart = GPA("alcCaptureStart"); + qalcCaptureStop = GPA("alcCaptureStop"); + qalcCaptureSamples = GPA("alcCaptureSamples"); + + if(alinit_fail) + { + QAL_Shutdown(); + Com_Printf( " One or more symbols not found\n"); + return qfalse; + } + + return qtrue; +} + +/* +================= +QAL_Shutdown +================= +*/ +void QAL_Shutdown( void ) +{ + if(OpenALLib) + { + Sys_UnloadLibrary(OpenALLib); + OpenALLib = NULL; + } + + qalEnable = NULL; + qalDisable = NULL; + qalIsEnabled = NULL; + qalGetString = NULL; + qalGetBooleanv = NULL; + qalGetIntegerv = NULL; + qalGetFloatv = NULL; + qalGetDoublev = NULL; + qalGetBoolean = NULL; + qalGetInteger = NULL; + qalGetFloat = NULL; + qalGetDouble = NULL; + qalGetError = NULL; + qalIsExtensionPresent = NULL; + qalGetProcAddress = NULL; + qalGetEnumValue = NULL; + qalListenerf = NULL; + qalListener3f = NULL; + qalListenerfv = NULL; + qalListeneri = NULL; + qalGetListenerf = NULL; + qalGetListener3f = NULL; + qalGetListenerfv = NULL; + qalGetListeneri = NULL; + qalGenSources = NULL; + qalDeleteSources = NULL; + qalIsSource = NULL; + qalSourcef = NULL; + qalSource3f = NULL; + qalSourcefv = NULL; + qalSourcei = NULL; + qalGetSourcef = NULL; + qalGetSource3f = NULL; + qalGetSourcefv = NULL; + qalGetSourcei = NULL; + qalSourcePlayv = NULL; + qalSourceStopv = NULL; + qalSourceRewindv = NULL; + qalSourcePausev = NULL; + qalSourcePlay = NULL; + qalSourceStop = NULL; + qalSourceRewind = NULL; + qalSourcePause = NULL; + qalSourceQueueBuffers = NULL; + qalSourceUnqueueBuffers = NULL; + qalGenBuffers = NULL; + qalDeleteBuffers = NULL; + qalIsBuffer = NULL; + qalBufferData = NULL; + qalGetBufferf = NULL; + qalGetBufferi = NULL; + qalDopplerFactor = NULL; + qalDopplerVelocity = NULL; + qalDistanceModel = NULL; + + qalcCreateContext = NULL; + qalcMakeContextCurrent = NULL; + qalcProcessContext = NULL; + qalcSuspendContext = NULL; + qalcDestroyContext = NULL; + qalcGetCurrentContext = NULL; + qalcGetContextsDevice = NULL; + qalcOpenDevice = NULL; + qalcCloseDevice = NULL; + qalcGetError = NULL; + qalcIsExtensionPresent = NULL; + qalcGetProcAddress = NULL; + qalcGetEnumValue = NULL; + qalcGetString = NULL; + qalcGetIntegerv = NULL; + qalcCaptureOpenDevice = NULL; + qalcCaptureCloseDevice = NULL; + qalcCaptureStart = NULL; + qalcCaptureStop = NULL; + qalcCaptureSamples = NULL; +} +#else +qboolean QAL_Init(const char *libname) +{ + return qtrue; +} +void QAL_Shutdown( void ) +{ +} +#endif +#endif diff --git a/reaction/engine/code/client/qal.h b/reaction/engine/code/client/qal.h new file mode 100644 index 00000000..1a2284a2 --- /dev/null +++ b/reaction/engine/code/client/qal.h @@ -0,0 +1,245 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com) + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + + +#ifndef __QAL_H__ +#define __QAL_H__ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" + +#ifdef USE_OPENAL_DLOPEN +#define AL_NO_PROTOTYPES +#define ALC_NO_PROTOTYPES +#endif + +#ifdef USE_LOCAL_HEADERS +#include "../AL/al.h" +#include "../AL/alc.h" +#else +#ifdef _MSC_VER + // MSVC users must install the OpenAL SDK which doesn't use the AL/*.h scheme. + #include + #include +#else + #include + #include +#endif +#endif + +#ifdef USE_OPENAL_DLOPEN +extern LPALENABLE qalEnable; +extern LPALDISABLE qalDisable; +extern LPALISENABLED qalIsEnabled; +extern LPALGETSTRING qalGetString; +extern LPALGETBOOLEANV qalGetBooleanv; +extern LPALGETINTEGERV qalGetIntegerv; +extern LPALGETFLOATV qalGetFloatv; +extern LPALGETDOUBLEV qalGetDoublev; +extern LPALGETBOOLEAN qalGetBoolean; +extern LPALGETINTEGER qalGetInteger; +extern LPALGETFLOAT qalGetFloat; +extern LPALGETDOUBLE qalGetDouble; +extern LPALGETERROR qalGetError; +extern LPALISEXTENSIONPRESENT qalIsExtensionPresent; +extern LPALGETPROCADDRESS qalGetProcAddress; +extern LPALGETENUMVALUE qalGetEnumValue; +extern LPALLISTENERF qalListenerf; +extern LPALLISTENER3F qalListener3f; +extern LPALLISTENERFV qalListenerfv; +extern LPALLISTENERI qalListeneri; +extern LPALLISTENER3I qalListener3i; +extern LPALLISTENERIV qalListeneriv; +extern LPALGETLISTENERF qalGetListenerf; +extern LPALGETLISTENER3F qalGetListener3f; +extern LPALGETLISTENERFV qalGetListenerfv; +extern LPALGETLISTENERI qalGetListeneri; +extern LPALGETLISTENER3I qalGetListener3i; +extern LPALGETLISTENERIV qalGetListeneriv; +extern LPALGENSOURCES qalGenSources; +extern LPALDELETESOURCES qalDeleteSources; +extern LPALISSOURCE qalIsSource; +extern LPALSOURCEF qalSourcef; +extern LPALSOURCE3F qalSource3f; +extern LPALSOURCEFV qalSourcefv; +extern LPALSOURCEI qalSourcei; +extern LPALSOURCE3I qalSource3i; +extern LPALSOURCEIV qalSourceiv; +extern LPALGETSOURCEF qalGetSourcef; +extern LPALGETSOURCE3F qalGetSource3f; +extern LPALGETSOURCEFV qalGetSourcefv; +extern LPALGETSOURCEI qalGetSourcei; +extern LPALGETSOURCE3I qalGetSource3i; +extern LPALGETSOURCEIV qalGetSourceiv; +extern LPALSOURCEPLAYV qalSourcePlayv; +extern LPALSOURCESTOPV qalSourceStopv; +extern LPALSOURCEREWINDV qalSourceRewindv; +extern LPALSOURCEPAUSEV qalSourcePausev; +extern LPALSOURCEPLAY qalSourcePlay; +extern LPALSOURCESTOP qalSourceStop; +extern LPALSOURCEREWIND qalSourceRewind; +extern LPALSOURCEPAUSE qalSourcePause; +extern LPALSOURCEQUEUEBUFFERS qalSourceQueueBuffers; +extern LPALSOURCEUNQUEUEBUFFERS qalSourceUnqueueBuffers; +extern LPALGENBUFFERS qalGenBuffers; +extern LPALDELETEBUFFERS qalDeleteBuffers; +extern LPALISBUFFER qalIsBuffer; +extern LPALBUFFERDATA qalBufferData; +extern LPALBUFFERF qalBufferf; +extern LPALBUFFER3F qalBuffer3f; +extern LPALBUFFERFV qalBufferfv; +extern LPALBUFFERF qalBufferi; +extern LPALBUFFER3F qalBuffer3i; +extern LPALBUFFERFV qalBufferiv; +extern LPALGETBUFFERF qalGetBufferf; +extern LPALGETBUFFER3F qalGetBuffer3f; +extern LPALGETBUFFERFV qalGetBufferfv; +extern LPALGETBUFFERI qalGetBufferi; +extern LPALGETBUFFER3I qalGetBuffer3i; +extern LPALGETBUFFERIV qalGetBufferiv; +extern LPALDOPPLERFACTOR qalDopplerFactor; +extern LPALDOPPLERVELOCITY qalDopplerVelocity; +extern LPALSPEEDOFSOUND qalSpeedOfSound; +extern LPALDISTANCEMODEL qalDistanceModel; + +extern LPALCCREATECONTEXT qalcCreateContext; +extern LPALCMAKECONTEXTCURRENT qalcMakeContextCurrent; +extern LPALCPROCESSCONTEXT qalcProcessContext; +extern LPALCSUSPENDCONTEXT qalcSuspendContext; +extern LPALCDESTROYCONTEXT qalcDestroyContext; +extern LPALCGETCURRENTCONTEXT qalcGetCurrentContext; +extern LPALCGETCONTEXTSDEVICE qalcGetContextsDevice; +extern LPALCOPENDEVICE qalcOpenDevice; +extern LPALCCLOSEDEVICE qalcCloseDevice; +extern LPALCGETERROR qalcGetError; +extern LPALCISEXTENSIONPRESENT qalcIsExtensionPresent; +extern LPALCGETPROCADDRESS qalcGetProcAddress; +extern LPALCGETENUMVALUE qalcGetEnumValue; +extern LPALCGETSTRING qalcGetString; +extern LPALCGETINTEGERV qalcGetIntegerv; +extern LPALCCAPTUREOPENDEVICE qalcCaptureOpenDevice; +extern LPALCCAPTURECLOSEDEVICE qalcCaptureCloseDevice; +extern LPALCCAPTURESTART qalcCaptureStart; +extern LPALCCAPTURESTOP qalcCaptureStop; +extern LPALCCAPTURESAMPLES qalcCaptureSamples; +#else +#define qalEnable alEnable +#define qalDisable alDisable +#define qalIsEnabled alIsEnabled +#define qalGetString alGetString +#define qalGetBooleanv alGetBooleanv +#define qalGetIntegerv alGetIntegerv +#define qalGetFloatv alGetFloatv +#define qalGetDoublev alGetDoublev +#define qalGetBoolean alGetBoolean +#define qalGetInteger alGetInteger +#define qalGetFloat alGetFloat +#define qalGetDouble alGetDouble +#define qalGetError alGetError +#define qalIsExtensionPresent alIsExtensionPresent +#define qalGetProcAddress alGetProcAddress +#define qalGetEnumValue alGetEnumValue +#define qalListenerf alListenerf +#define qalListener3f alListener3f +#define qalListenerfv alListenerfv +#define qalListeneri alListeneri +#define qalListener3i alListener3i +#define qalListeneriv alListeneriv +#define qalGetListenerf alGetListenerf +#define qalGetListener3f alGetListener3f +#define qalGetListenerfv alGetListenerfv +#define qalGetListeneri alGetListeneri +#define qalGetListener3i alGetListener3i +#define qalGetListeneriv alGetListeneriv +#define qalGenSources alGenSources +#define qalDeleteSources alDeleteSources +#define qalIsSource alIsSource +#define qalSourcef alSourcef +#define qalSource3f alSource3f +#define qalSourcefv alSourcefv +#define qalSourcei alSourcei +#define qalSource3i alSource3i +#define qalSourceiv alSourceiv +#define qalGetSourcef alGetSourcef +#define qalGetSource3f alGetSource3f +#define qalGetSourcefv alGetSourcefv +#define qalGetSourcei alGetSourcei +#define qalGetSource3i alGetSource3i +#define qalGetSourceiv alGetSourceiv +#define qalSourcePlayv alSourcePlayv +#define qalSourceStopv alSourceStopv +#define qalSourceRewindv alSourceRewindv +#define qalSourcePausev alSourcePausev +#define qalSourcePlay alSourcePlay +#define qalSourceStop alSourceStop +#define qalSourceRewind alSourceRewind +#define qalSourcePause alSourcePause +#define qalSourceQueueBuffers alSourceQueueBuffers +#define qalSourceUnqueueBuffers alSourceUnqueueBuffers +#define qalGenBuffers alGenBuffers +#define qalDeleteBuffers alDeleteBuffers +#define qalIsBuffer alIsBuffer +#define qalBufferData alBufferData +#define qalBufferf alBufferf +#define qalBuffer3f alBuffer3f +#define qalBufferfv alBufferfv +#define qalBufferi alBufferi +#define qalBuffer3i alBuffer3i +#define qalBufferiv alBufferiv +#define qalGetBufferf alGetBufferf +#define qalGetBuffer3f alGetBuffer3f +#define qalGetBufferfv alGetBufferfv +#define qalGetBufferi alGetBufferi +#define qalGetBuffer3i alGetBuffer3i +#define qalGetBufferiv alGetBufferiv +#define qalDopplerFactor alDopplerFactor +#define qalDopplerVelocity alDopplerVelocity +#define qalSpeedOfSound alSpeedOfSound +#define qalDistanceModel alDistanceModel + +#define qalcCreateContext alcCreateContext +#define qalcMakeContextCurrent alcMakeContextCurrent +#define qalcProcessContext alcProcessContext +#define qalcSuspendContext alcSuspendContext +#define qalcDestroyContext alcDestroyContext +#define qalcGetCurrentContext alcGetCurrentContext +#define qalcGetContextsDevice alcGetContextsDevice +#define qalcOpenDevice alcOpenDevice +#define qalcCloseDevice alcCloseDevice +#define qalcGetError alcGetError +#define qalcIsExtensionPresent alcIsExtensionPresent +#define qalcGetProcAddress alcGetProcAddress +#define qalcGetEnumValue alcGetEnumValue +#define qalcGetString alcGetString +#define qalcGetIntegerv alcGetIntegerv +#define qalcCaptureOpenDevice alcCaptureOpenDevice +#define qalcCaptureCloseDevice alcCaptureCloseDevice +#define qalcCaptureStart alcCaptureStart +#define qalcCaptureStop alcCaptureStop +#define qalcCaptureSamples alcCaptureSamples +#endif + +qboolean QAL_Init(const char *libname); +void QAL_Shutdown( void ); + +#endif // __QAL_H__ diff --git a/reaction/engine/code/client/snd_adpcm.c b/reaction/engine/code/client/snd_adpcm.c new file mode 100644 index 00000000..89e68f42 --- /dev/null +++ b/reaction/engine/code/client/snd_adpcm.c @@ -0,0 +1,330 @@ +/*********************************************************** +Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* +** Intel/DVI ADPCM coder/decoder. +** +** The algorithm for this coder was taken from the IMA Compatability Project +** proceedings, Vol 2, Number 2; May 1992. +** +** Version 1.2, 18-Dec-92. +*/ + +#include "snd_local.h" + + +/* Intel ADPCM step variation table */ +static int indexTable[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8, +}; + +static int stepsizeTable[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 +}; + + +void S_AdpcmEncode( short indata[], char outdata[], int len, struct adpcm_state *state ) { + short *inp; /* Input buffer pointer */ + signed char *outp; /* output buffer pointer */ + int val; /* Current input sample value */ + int sign; /* Current adpcm sign bit */ + int delta; /* Current adpcm output value */ + int diff; /* Difference between val and sample */ + int step; /* Stepsize */ + int valpred; /* Predicted output value */ + int vpdiff; /* Current change to valpred */ + int index; /* Current step change index */ + int outputbuffer; /* place to keep previous 4-bit value */ + int bufferstep; /* toggle between outputbuffer/output */ + + outp = (signed char *)outdata; + inp = indata; + + valpred = state->sample; + index = state->index; + step = stepsizeTable[index]; + + outputbuffer = 0; // quiet a compiler warning + bufferstep = 1; + + for ( ; len > 0 ; len-- ) { + val = *inp++; + + /* Step 1 - compute difference with previous value */ + diff = val - valpred; + sign = (diff < 0) ? 8 : 0; + if ( sign ) diff = (-diff); + + /* Step 2 - Divide and clamp */ + /* Note: + ** This code *approximately* computes: + ** delta = diff*4/step; + ** vpdiff = (delta+0.5)*step/4; + ** but in shift step bits are dropped. The net result of this is + ** that even if you have fast mul/div hardware you cannot put it to + ** good use since the fixup would be too expensive. + */ + delta = 0; + vpdiff = (step >> 3); + + if ( diff >= step ) { + delta = 4; + diff -= step; + vpdiff += step; + } + step >>= 1; + if ( diff >= step ) { + delta |= 2; + diff -= step; + vpdiff += step; + } + step >>= 1; + if ( diff >= step ) { + delta |= 1; + vpdiff += step; + } + + /* Step 3 - Update previous value */ + if ( sign ) + valpred -= vpdiff; + else + valpred += vpdiff; + + /* Step 4 - Clamp previous value to 16 bits */ + if ( valpred > 32767 ) + valpred = 32767; + else if ( valpred < -32768 ) + valpred = -32768; + + /* Step 5 - Assemble value, update index and step values */ + delta |= sign; + + index += indexTable[delta]; + if ( index < 0 ) index = 0; + if ( index > 88 ) index = 88; + step = stepsizeTable[index]; + + /* Step 6 - Output value */ + if ( bufferstep ) { + outputbuffer = (delta << 4) & 0xf0; + } else { + *outp++ = (delta & 0x0f) | outputbuffer; + } + bufferstep = !bufferstep; + } + + /* Output last step, if needed */ + if ( !bufferstep ) + *outp++ = outputbuffer; + + state->sample = valpred; + state->index = index; +} + + +/* static */ void S_AdpcmDecode( const char indata[], short *outdata, int len, struct adpcm_state *state ) { + signed char *inp; /* Input buffer pointer */ + int outp; /* output buffer pointer */ + int sign; /* Current adpcm sign bit */ + int delta; /* Current adpcm output value */ + int step; /* Stepsize */ + int valpred; /* Predicted value */ + int vpdiff; /* Current change to valpred */ + int index; /* Current step change index */ + int inputbuffer; /* place to keep next 4-bit value */ + int bufferstep; /* toggle between inputbuffer/input */ + + outp = 0; + inp = (signed char *)indata; + + valpred = state->sample; + index = state->index; + step = stepsizeTable[index]; + + bufferstep = 0; + inputbuffer = 0; // quiet a compiler warning + for ( ; len > 0 ; len-- ) { + + /* Step 1 - get the delta value */ + if ( bufferstep ) { + delta = inputbuffer & 0xf; + } else { + inputbuffer = *inp++; + delta = (inputbuffer >> 4) & 0xf; + } + bufferstep = !bufferstep; + + /* Step 2 - Find new index value (for later) */ + index += indexTable[delta]; + if ( index < 0 ) index = 0; + if ( index > 88 ) index = 88; + + /* Step 3 - Separate sign and magnitude */ + sign = delta & 8; + delta = delta & 7; + + /* Step 4 - Compute difference and new predicted value */ + /* + ** Computes 'vpdiff = (delta+0.5)*step/4', but see comment + ** in adpcm_coder. + */ + vpdiff = step >> 3; + if ( delta & 4 ) vpdiff += step; + if ( delta & 2 ) vpdiff += step>>1; + if ( delta & 1 ) vpdiff += step>>2; + + if ( sign ) + valpred -= vpdiff; + else + valpred += vpdiff; + + /* Step 5 - clamp output value */ + if ( valpred > 32767 ) + valpred = 32767; + else if ( valpred < -32768 ) + valpred = -32768; + + /* Step 6 - Update step value */ + step = stepsizeTable[index]; + + /* Step 7 - Output value */ + outdata[outp] = valpred; + outp++; + } + + state->sample = valpred; + state->index = index; +} + + +/* +==================== +S_AdpcmMemoryNeeded + +Returns the amount of memory (in bytes) needed to store the samples in out internal adpcm format +==================== +*/ +int S_AdpcmMemoryNeeded( const wavinfo_t *info ) { + float scale; + int scaledSampleCount; + int sampleMemory; + int blockCount; + int headerMemory; + + // determine scale to convert from input sampling rate to desired sampling rate + scale = (float)info->rate / dma.speed; + + // calc number of samples at playback sampling rate + scaledSampleCount = info->samples / scale; + + // calc memory need to store those samples using ADPCM at 4 bits per sample + sampleMemory = scaledSampleCount / 2; + + // calc number of sample blocks needed of PAINTBUFFER_SIZE + blockCount = scaledSampleCount / PAINTBUFFER_SIZE; + if( scaledSampleCount % PAINTBUFFER_SIZE ) { + blockCount++; + } + + // calc memory needed to store the block headers + headerMemory = blockCount * sizeof(adpcm_state_t); + + return sampleMemory + headerMemory; +} + + +/* +==================== +S_AdpcmGetSamples +==================== +*/ +void S_AdpcmGetSamples(sndBuffer *chunk, short *to) { + adpcm_state_t state; + byte *out; + + // get the starting state from the block header + state.index = chunk->adpcm.index; + state.sample = chunk->adpcm.sample; + + out = (byte *)chunk->sndChunk; + // get samples + S_AdpcmDecode((char *) out, to, SND_CHUNK_SIZE_BYTE*2, &state ); +} + + +/* +==================== +S_AdpcmEncodeSound +==================== +*/ +void S_AdpcmEncodeSound( sfx_t *sfx, short *samples ) { + adpcm_state_t state; + int inOffset; + int count; + int n; + sndBuffer *newchunk, *chunk; + byte *out; + + inOffset = 0; + count = sfx->soundLength; + state.index = 0; + state.sample = samples[0]; + + chunk = NULL; + while( count ) { + n = count; + if( n > SND_CHUNK_SIZE_BYTE*2 ) { + n = SND_CHUNK_SIZE_BYTE*2; + } + + newchunk = SND_malloc(); + if (sfx->soundData == NULL) { + sfx->soundData = newchunk; + } else { + chunk->next = newchunk; + } + chunk = newchunk; + + // output the header + chunk->adpcm.index = state.index; + chunk->adpcm.sample = state.sample; + + out = (byte *)chunk->sndChunk; + + // encode the samples + S_AdpcmEncode( samples + inOffset, (char *) out, n, &state ); + + inOffset += n; + count -= n; + } +} diff --git a/reaction/engine/code/client/snd_codec.c b/reaction/engine/code/client/snd_codec.c new file mode 100644 index 00000000..0483e8df --- /dev/null +++ b/reaction/engine/code/client/snd_codec.c @@ -0,0 +1,237 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com) + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "client.h" +#include "snd_codec.h" + +static snd_codec_t *codecs; + +/* +================= +S_FileExtension +================= +*/ +static char *S_FileExtension(const char *fni) +{ + // we should search from the ending to the last '/' + + char *fn = (char *) fni + strlen(fni) - 1; + char *eptr = NULL; + + while(*fn != '/' && fn != fni) + { + if(*fn == '.') + { + eptr = fn; + break; + } + fn--; + } + + return eptr; +} + +/* +================= +S_FindCodecForFile + +Select an appropriate codec for a file based on its extension +================= +*/ +static snd_codec_t *S_FindCodecForFile(const char *filename) +{ + char *ext = S_FileExtension(filename); + snd_codec_t *codec = codecs; + + if(!ext) + { + // No extension - auto-detect + while(codec) + { + char fn[MAX_QPATH]; + + // there is no extension so we do not need to subtract 4 chars + Q_strncpyz(fn, filename, MAX_QPATH); + COM_DefaultExtension(fn, MAX_QPATH, codec->ext); + + // Check it exists + if(FS_ReadFile(fn, NULL) != -1) + return codec; + + // Nope. Next! + codec = codec->next; + } + + // Nothin' + return NULL; + } + + while(codec) + { + if(!Q_stricmp(ext, codec->ext)) + return codec; + codec = codec->next; + } + + return NULL; +} + +/* +================= +S_CodecInit +================= +*/ +void S_CodecInit() +{ + codecs = NULL; + S_CodecRegister(&wav_codec); +#ifdef USE_CODEC_VORBIS + S_CodecRegister(&ogg_codec); +#endif +} + +/* +================= +S_CodecShutdown +================= +*/ +void S_CodecShutdown() +{ + codecs = NULL; +} + +/* +================= +S_CodecRegister +================= +*/ +void S_CodecRegister(snd_codec_t *codec) +{ + codec->next = codecs; + codecs = codec; +} + +/* +================= +S_CodecLoad +================= +*/ +void *S_CodecLoad(const char *filename, snd_info_t *info) +{ + snd_codec_t *codec; + char fn[MAX_QPATH]; + + codec = S_FindCodecForFile(filename); + if(!codec) + { + Com_Printf("Unknown extension for %s\n", filename); + return NULL; + } + + strncpy(fn, filename, sizeof(fn)); + COM_DefaultExtension(fn, sizeof(fn), codec->ext); + + return codec->load(fn, info); +} + +/* +================= +S_CodecOpenStream +================= +*/ +snd_stream_t *S_CodecOpenStream(const char *filename) +{ + snd_codec_t *codec; + char fn[MAX_QPATH]; + + codec = S_FindCodecForFile(filename); + if(!codec) + { + Com_Printf("Unknown extension for %s\n", filename); + return NULL; + } + + strncpy(fn, filename, sizeof(fn)); + COM_DefaultExtension(fn, sizeof(fn), codec->ext); + + return codec->open(fn); +} + +void S_CodecCloseStream(snd_stream_t *stream) +{ + stream->codec->close(stream); +} + +int S_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer) +{ + return stream->codec->read(stream, bytes, buffer); +} + +//======================================================================= +// Util functions (used by codecs) + +/* +================= +S_CodecUtilOpen +================= +*/ +snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec) +{ + snd_stream_t *stream; + fileHandle_t hnd; + int length; + + // Try to open the file + length = FS_FOpenFileRead(filename, &hnd, qtrue); + if(!hnd) + { + Com_Printf("Can't read sound file %s\n", filename); + return NULL; + } + + // Allocate a stream + stream = Z_Malloc(sizeof(snd_stream_t)); + if(!stream) + { + FS_FCloseFile(hnd); + return NULL; + } + + // Copy over, return + stream->codec = codec; + stream->file = hnd; + stream->length = length; + return stream; +} + +/* +================= +S_CodecUtilClose +================= +*/ +void S_CodecUtilClose(snd_stream_t **stream) +{ + FS_FCloseFile((*stream)->file); + Z_Free(*stream); + *stream = NULL; +} diff --git a/reaction/engine/code/client/snd_codec.h b/reaction/engine/code/client/snd_codec.h new file mode 100644 index 00000000..03fcaa25 --- /dev/null +++ b/reaction/engine/code/client/snd_codec.h @@ -0,0 +1,98 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com) + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#ifndef _SND_CODEC_H_ +#define _SND_CODEC_H_ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" + +typedef struct snd_info_s +{ + int rate; + int width; + int channels; + int samples; + int size; + int dataofs; +} snd_info_t; + +typedef struct snd_codec_s snd_codec_t; + +typedef struct snd_stream_s +{ + snd_codec_t *codec; + fileHandle_t file; + snd_info_t info; + int length; + int pos; + void *ptr; +} snd_stream_t; + +// Codec functions +typedef void *(*CODEC_LOAD)(const char *filename, snd_info_t *info); +typedef snd_stream_t *(*CODEC_OPEN)(const char *filename); +typedef int (*CODEC_READ)(snd_stream_t *stream, int bytes, void *buffer); +typedef void (*CODEC_CLOSE)(snd_stream_t *stream); + +// Codec data structure +struct snd_codec_s +{ + char *ext; + CODEC_LOAD load; + CODEC_OPEN open; + CODEC_READ read; + CODEC_CLOSE close; + snd_codec_t *next; +}; + +// Codec management +void S_CodecInit( void ); +void S_CodecShutdown( void ); +void S_CodecRegister(snd_codec_t *codec); +void *S_CodecLoad(const char *filename, snd_info_t *info); +snd_stream_t *S_CodecOpenStream(const char *filename); +void S_CodecCloseStream(snd_stream_t *stream); +int S_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer); + +// Util functions (used by codecs) +snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec); +void S_CodecUtilClose(snd_stream_t **stream); + +// WAV Codec +extern snd_codec_t wav_codec; +void *S_WAV_CodecLoad(const char *filename, snd_info_t *info); +snd_stream_t *S_WAV_CodecOpenStream(const char *filename); +void S_WAV_CodecCloseStream(snd_stream_t *stream); +int S_WAV_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer); + +// Ogg Vorbis codec +#ifdef USE_CODEC_VORBIS +extern snd_codec_t ogg_codec; +void *S_OGG_CodecLoad(const char *filename, snd_info_t *info); +snd_stream_t *S_OGG_CodecOpenStream(const char *filename); +void S_OGG_CodecCloseStream(snd_stream_t *stream); +int S_OGG_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer); +#endif // USE_CODEC_VORBIS + +#endif // !_SND_CODEC_H_ diff --git a/reaction/engine/code/client/snd_codec_ogg.c b/reaction/engine/code/client/snd_codec_ogg.c new file mode 100644 index 00000000..48da5d73 --- /dev/null +++ b/reaction/engine/code/client/snd_codec_ogg.c @@ -0,0 +1,477 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com) +Copyright (C) 2005-2006 Joerg Dietrich + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +// OGG support is enabled by this define +#ifdef USE_CODEC_VORBIS + +// includes for the Q3 sound system +#include "client.h" +#include "snd_codec.h" + +// includes for the OGG codec +#include +#include + +// The OGG codec can return the samples in a number of different formats, +// we use the standard signed short format. +#define OGG_SAMPLEWIDTH 2 + +// Q3 OGG codec +snd_codec_t ogg_codec = +{ + ".ogg", + S_OGG_CodecLoad, + S_OGG_CodecOpenStream, + S_OGG_CodecReadStream, + S_OGG_CodecCloseStream, + NULL +}; + +// callbacks for vobisfile + +// fread() replacement +size_t S_OGG_Callback_read(void *ptr, size_t size, size_t nmemb, void *datasource) +{ + snd_stream_t *stream; + int byteSize = 0; + int bytesRead = 0; + size_t nMembRead = 0; + + // check if input is valid + if(!ptr) + { + errno = EFAULT; + return 0; + } + + if(!(size && nmemb)) + { + // It's not an error, caller just wants zero bytes! + errno = 0; + return 0; + } + + if(!datasource) + { + errno = EBADF; + return 0; + } + + // we use a snd_stream_t in the generic pointer to pass around + stream = (snd_stream_t *) datasource; + + // FS_Read does not support multi-byte elements + byteSize = nmemb * size; + + // read it with the Q3 function FS_Read() + bytesRead = FS_Read(ptr, byteSize, stream->file); + + // update the file position + stream->pos += bytesRead; + + // this function returns the number of elements read not the number of bytes + nMembRead = bytesRead / size; + + // even if the last member is only read partially + // it is counted as a whole in the return value + if(bytesRead % size) + { + nMembRead++; + } + + return nMembRead; +} + +// fseek() replacement +int S_OGG_Callback_seek(void *datasource, ogg_int64_t offset, int whence) +{ + snd_stream_t *stream; + int retVal = 0; + + // check if input is valid + if(!datasource) + { + errno = EBADF; + return -1; + } + + // snd_stream_t in the generic pointer + stream = (snd_stream_t *) datasource; + + // we must map the whence to its Q3 counterpart + switch(whence) + { + case SEEK_SET : + { + // set the file position in the actual file with the Q3 function + retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_SET); + + // something has gone wrong, so we return here + if(retVal < 0) + { + return retVal; + } + + // keep track of file position + stream->pos = (int) offset; + break; + } + + case SEEK_CUR : + { + // set the file position in the actual file with the Q3 function + retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_CUR); + + // something has gone wrong, so we return here + if(retVal < 0) + { + return retVal; + } + + // keep track of file position + stream->pos += (int) offset; + break; + } + + case SEEK_END : + { + // Quake 3 seems to have trouble with FS_SEEK_END + // so we use the file length and FS_SEEK_SET + + // set the file position in the actual file with the Q3 function + retVal = FS_Seek(stream->file, (long) stream->length + (long) offset, FS_SEEK_SET); + + // something has gone wrong, so we return here + if(retVal < 0) + { + return retVal; + } + + // keep track of file position + stream->pos = stream->length + (int) offset; + break; + } + + default : + { + // unknown whence, so we return an error + errno = EINVAL; + return -1; + } + } + + // stream->pos shouldn't be smaller than zero or bigger than the filesize + stream->pos = (stream->pos < 0) ? 0 : stream->pos; + stream->pos = (stream->pos > stream->length) ? stream->length : stream->pos; + + return 0; +} + +// fclose() replacement +int S_OGG_Callback_close(void *datasource) +{ + // we do nothing here and close all things manually in S_OGG_CodecCloseStream() + return 0; +} + +// ftell() replacement +long S_OGG_Callback_tell(void *datasource) +{ + snd_stream_t *stream; + + // check if input is valid + if(!datasource) + { + errno = EBADF; + return -1; + } + + // snd_stream_t in the generic pointer + stream = (snd_stream_t *) datasource; + + return (long) FS_FTell(stream->file); +} + +// the callback structure +const ov_callbacks S_OGG_Callbacks = +{ + &S_OGG_Callback_read, + &S_OGG_Callback_seek, + &S_OGG_Callback_close, + &S_OGG_Callback_tell +}; + +/* +================= +S_OGG_CodecOpenStream +================= +*/ +snd_stream_t *S_OGG_CodecOpenStream(const char *filename) +{ + snd_stream_t *stream; + + // OGG codec control structure + OggVorbis_File *vf; + + // some variables used to get informations about the OGG + vorbis_info *OGGInfo; + ogg_int64_t numSamples; + + // check if input is valid + if(!filename) + { + return NULL; + } + + // Open the stream + stream = S_CodecUtilOpen(filename, &ogg_codec); + if(!stream) + { + return NULL; + } + + // alloctate the OggVorbis_File + vf = Z_Malloc(sizeof(OggVorbis_File)); + if(!vf) + { + S_CodecUtilClose(&stream); + + return NULL; + } + + // open the codec with our callbacks and stream as the generic pointer + if(ov_open_callbacks(stream, vf, NULL, 0, S_OGG_Callbacks) != 0) + { + Z_Free(vf); + + S_CodecUtilClose(&stream); + + return NULL; + } + + // the stream must be seekable + if(!ov_seekable(vf)) + { + ov_clear(vf); + + Z_Free(vf); + + S_CodecUtilClose(&stream); + + return NULL; + } + + // we only support OGGs with one substream + if(ov_streams(vf) != 1) + { + ov_clear(vf); + + Z_Free(vf); + + S_CodecUtilClose(&stream); + + return NULL; + } + + // get the info about channels and rate + OGGInfo = ov_info(vf, 0); + if(!OGGInfo) + { + ov_clear(vf); + + Z_Free(vf); + + S_CodecUtilClose(&stream); + + return NULL; + } + + // get the number of sample-frames in the OGG + numSamples = ov_pcm_total(vf, 0); + + // fill in the info-structure in the stream + stream->info.rate = OGGInfo->rate; + stream->info.width = OGG_SAMPLEWIDTH; + stream->info.channels = OGGInfo->channels; + stream->info.samples = numSamples; + stream->info.size = stream->info.samples * stream->info.channels * stream->info.width; + stream->info.dataofs = 0; + + // We use stream->pos for the file pointer in the compressed ogg file + stream->pos = 0; + + // We use the generic pointer in stream for the OGG codec control structure + stream->ptr = vf; + + return stream; +} + +/* +================= +S_OGG_CodecCloseStream +================= +*/ +void S_OGG_CodecCloseStream(snd_stream_t *stream) +{ + // check if input is valid + if(!stream) + { + return; + } + + // let the OGG codec cleanup its stuff + ov_clear((OggVorbis_File *) stream->ptr); + + // free the OGG codec control struct + Z_Free(stream->ptr); + + // close the stream + S_CodecUtilClose(&stream); +} + +/* +================= +S_OGG_CodecReadStream +================= +*/ +int S_OGG_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer) +{ + // buffer handling + int bytesRead, bytesLeft, c; + char *bufPtr; + + // Bitstream for the decoder + int BS = 0; + + // big endian machines want their samples in big endian order + int IsBigEndian = 0; + +# ifdef Q3_BIG_ENDIAN + IsBigEndian = 1; +# endif // Q3_BIG_ENDIAN + + // check if input is valid + if(!(stream && buffer)) + { + return 0; + } + + if(bytes <= 0) + { + return 0; + } + + bytesRead = 0; + bytesLeft = bytes; + bufPtr = buffer; + + // cycle until we have the requested or all available bytes read + while(-1) + { + // read some bytes from the OGG codec + c = ov_read((OggVorbis_File *) stream->ptr, bufPtr, bytesLeft, IsBigEndian, OGG_SAMPLEWIDTH, 1, &BS); + + // no more bytes are left + if(c <= 0) + { + break; + } + + bytesRead += c; + bytesLeft -= c; + bufPtr += c; + + // we have enough bytes + if(bytesLeft <= 0) + { + break; + } + } + + return bytesRead; +} + +/* +===================================================================== +S_OGG_CodecLoad + +We handle S_OGG_CodecLoad as a special case of the streaming functions +where we read the whole stream at once. +====================================================================== +*/ +void *S_OGG_CodecLoad(const char *filename, snd_info_t *info) +{ + snd_stream_t *stream; + byte *buffer; + int bytesRead; + + // check if input is valid + if(!(filename && info)) + { + return NULL; + } + + // open the file as a stream + stream = S_OGG_CodecOpenStream(filename); + if(!stream) + { + return NULL; + } + + // copy over the info + info->rate = stream->info.rate; + info->width = stream->info.width; + info->channels = stream->info.channels; + info->samples = stream->info.samples; + info->size = stream->info.size; + info->dataofs = stream->info.dataofs; + + // allocate a buffer + // this buffer must be free-ed by the caller of this function + buffer = Z_Malloc(info->size); + if(!buffer) + { + S_OGG_CodecCloseStream(stream); + + return NULL; + } + + // fill the buffer + bytesRead = S_OGG_CodecReadStream(stream, info->size, buffer); + + // we don't even have read a single byte + if(bytesRead <= 0) + { + Z_Free(buffer); + S_OGG_CodecCloseStream(stream); + + return NULL; + } + + S_OGG_CodecCloseStream(stream); + + return buffer; +} + +#endif // USE_CODEC_VORBIS diff --git a/reaction/engine/code/client/snd_codec_wav.c b/reaction/engine/code/client/snd_codec_wav.c new file mode 100644 index 00000000..c0b2e2e2 --- /dev/null +++ b/reaction/engine/code/client/snd_codec_wav.c @@ -0,0 +1,294 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com) + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "client.h" +#include "snd_codec.h" + +/* +================= +FGetLittleLong +================= +*/ +static int FGetLittleLong( fileHandle_t f ) { + int v; + + FS_Read( &v, sizeof(v), f ); + + return LittleLong( v); +} + +/* +================= +FGetLittleShort +================= +*/ +static short FGetLittleShort( fileHandle_t f ) { + short v; + + FS_Read( &v, sizeof(v), f ); + + return LittleShort( v); +} + +/* +================= +S_ReadChunkInfo +================= +*/ +static int S_ReadChunkInfo(fileHandle_t f, char *name) +{ + int len, r; + + name[4] = 0; + + r = FS_Read(name, 4, f); + if(r != 4) + return -1; + + len = FGetLittleLong(f); + if( len < 0 ) { + Com_Printf( S_COLOR_YELLOW "WARNING: Negative chunk length\n" ); + return -1; + } + + return len; +} + +/* +================= +S_FindRIFFChunk + +Returns the length of the data in the chunk, or -1 if not found +================= +*/ +static int S_FindRIFFChunk( fileHandle_t f, char *chunk ) { + char name[5]; + int len; + + while( ( len = S_ReadChunkInfo(f, name) ) >= 0 ) + { + // If this is the right chunk, return + if( !Q_strncmp( name, chunk, 4 ) ) + return len; + + len = PAD( len, 2 ); + + // Not the right chunk - skip it + FS_Seek( f, len, FS_SEEK_CUR ); + } + + return -1; +} + +/* +================= +S_ByteSwapRawSamples +================= +*/ +static void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *data ) { + int i; + + if ( width != 2 ) { + return; + } + if ( LittleShort( 256 ) == 256 ) { + return; + } + + if ( s_channels == 2 ) { + samples <<= 1; + } + for ( i = 0 ; i < samples ; i++ ) { + ((short *)data)[i] = LittleShort( ((short *)data)[i] ); + } +} + +/* +================= +S_ReadRIFFHeader +================= +*/ +static qboolean S_ReadRIFFHeader(fileHandle_t file, snd_info_t *info) +{ + char dump[16]; + int wav_format; + int bits; + int fmtlen = 0; + + // skip the riff wav header + FS_Read(dump, 12, file); + + // Scan for the format chunk + if((fmtlen = S_FindRIFFChunk(file, "fmt ")) < 0) + { + Com_Printf( S_COLOR_RED "ERROR: Couldn't find \"fmt\" chunk\n"); + return qfalse; + } + + // Save the parameters + wav_format = FGetLittleShort(file); + info->channels = FGetLittleShort(file); + info->rate = FGetLittleLong(file); + FGetLittleLong(file); + FGetLittleShort(file); + bits = FGetLittleShort(file); + + if( bits < 8 ) + { + Com_Printf( S_COLOR_RED "ERROR: Less than 8 bit sound is not supported\n"); + return qfalse; + } + + info->width = bits / 8; + info->dataofs = 0; + + // Skip the rest of the format chunk if required + if(fmtlen > 16) + { + fmtlen -= 16; + FS_Seek( file, fmtlen, FS_SEEK_CUR ); + } + + // Scan for the data chunk + if( (info->size = S_FindRIFFChunk(file, "data")) < 0) + { + Com_Printf( S_COLOR_RED "ERROR: Couldn't find \"data\" chunk\n"); + return qfalse; + } + info->samples = (info->size / info->width) / info->channels; + + return qtrue; +} + +// WAV codec +snd_codec_t wav_codec = +{ + ".wav", + S_WAV_CodecLoad, + S_WAV_CodecOpenStream, + S_WAV_CodecReadStream, + S_WAV_CodecCloseStream, + NULL +}; + +/* +================= +S_WAV_CodecLoad +================= +*/ +void *S_WAV_CodecLoad(const char *filename, snd_info_t *info) +{ + fileHandle_t file; + void *buffer; + + // Try to open the file + FS_FOpenFileRead(filename, &file, qtrue); + if(!file) + { + Com_Printf( S_COLOR_RED "ERROR: Could not open \"%s\"\n", + filename); + return NULL; + } + + // Read the RIFF header + if(!S_ReadRIFFHeader(file, info)) + { + FS_FCloseFile(file); + Com_Printf( S_COLOR_RED "ERROR: Incorrect/unsupported format in \"%s\"\n", + filename); + return NULL; + } + + // Allocate some memory + buffer = Z_Malloc(info->size); + if(!buffer) + { + FS_FCloseFile(file); + Com_Printf( S_COLOR_RED "ERROR: Out of memory reading \"%s\"\n", + filename); + return NULL; + } + + // Read, byteswap + FS_Read(buffer, info->size, file); + S_ByteSwapRawSamples(info->samples, info->width, info->channels, (byte *)buffer); + + // Close and return + FS_FCloseFile(file); + return buffer; +} + +/* +================= +S_WAV_CodecOpenStream +================= +*/ +snd_stream_t *S_WAV_CodecOpenStream(const char *filename) +{ + snd_stream_t *rv; + + // Open + rv = S_CodecUtilOpen(filename, &wav_codec); + if(!rv) + return NULL; + + // Read the RIFF header + if(!S_ReadRIFFHeader(rv->file, &rv->info)) + { + S_CodecUtilClose(&rv); + return NULL; + } + + return rv; +} + +/* +================= +S_WAV_CodecCloseStream +================= +*/ +void S_WAV_CodecCloseStream(snd_stream_t *stream) +{ + S_CodecUtilClose(&stream); +} + +/* +================= +S_WAV_CodecReadStream +================= +*/ +int S_WAV_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer) +{ + int remaining = stream->info.size - stream->pos; + int samples; + + if(remaining <= 0) + return 0; + if(bytes > remaining) + bytes = remaining; + stream->pos += bytes; + samples = (bytes / stream->info.width) / stream->info.channels; + FS_Read(buffer, bytes, stream->file); + S_ByteSwapRawSamples(samples, stream->info.width, stream->info.channels, buffer); + return bytes; +} diff --git a/reaction/engine/code/client/snd_dma.c b/reaction/engine/code/client/snd_dma.c new file mode 100644 index 00000000..acda0f52 --- /dev/null +++ b/reaction/engine/code/client/snd_dma.c @@ -0,0 +1,1541 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: snd_dma.c + * + * desc: main control for any streaming sound output device + * + * $Archive: /MissionPack/code/client/snd_dma.c $ + * + *****************************************************************************/ + +#include "snd_local.h" +#include "snd_codec.h" +#include "client.h" + +void S_Play_f(void); +void S_SoundList_f(void); +void S_Music_f(void); + +void S_Update_( void ); +void S_Base_StopAllSounds(void); +void S_Base_StopBackgroundTrack( void ); + +snd_stream_t *s_backgroundStream = NULL; +static char s_backgroundLoop[MAX_QPATH]; +//static char s_backgroundMusic[MAX_QPATH]; //TTimo: unused + + +// ======================================================================= +// Internal sound data & structures +// ======================================================================= + +// only begin attenuating sound volumes when outside the FULLVOLUME range +#define SOUND_FULLVOLUME 80 + +#define SOUND_ATTENUATE 0.0008f + +channel_t s_channels[MAX_CHANNELS]; +channel_t loop_channels[MAX_CHANNELS]; +int numLoopChannels; + +static int s_soundStarted; +static qboolean s_soundMuted; + +dma_t dma; + +static int listener_number; +static vec3_t listener_origin; +static vec3_t listener_axis[3]; + +int s_soundtime; // sample PAIRS +int s_paintedtime; // sample PAIRS + +// MAX_SFX may be larger than MAX_SOUNDS because +// of custom player sounds +#define MAX_SFX 4096 +sfx_t s_knownSfx[MAX_SFX]; +int s_numSfx = 0; + +#define LOOP_HASH 128 +static sfx_t *sfxHash[LOOP_HASH]; + +cvar_t *s_testsound; +cvar_t *s_khz; +cvar_t *s_show; +cvar_t *s_mixahead; +cvar_t *s_mixPreStep; + +static loopSound_t loopSounds[MAX_GENTITIES]; +static channel_t *freelist = NULL; + +int s_rawend[MAX_RAW_STREAMS]; +portable_samplepair_t s_rawsamples[MAX_RAW_STREAMS][MAX_RAW_SAMPLES]; + + +// ==================================================================== +// User-setable variables +// ==================================================================== + + +void S_Base_SoundInfo(void) { + Com_Printf("----- Sound Info -----\n" ); + if (!s_soundStarted) { + Com_Printf ("sound system not started\n"); + } else { + Com_Printf("%5d stereo\n", dma.channels - 1); + Com_Printf("%5d samples\n", dma.samples); + Com_Printf("%5d samplebits\n", dma.samplebits); + Com_Printf("%5d submission_chunk\n", dma.submission_chunk); + Com_Printf("%5d speed\n", dma.speed); + Com_Printf("%p dma buffer\n", dma.buffer); + if ( s_backgroundStream ) { + Com_Printf("Background file: %s\n", s_backgroundLoop ); + } else { + Com_Printf("No background file.\n" ); + } + + } + Com_Printf("----------------------\n" ); +} + + +#ifdef USE_VOIP +static +void S_Base_StartCapture( void ) +{ + // !!! FIXME: write me. +} + +static +int S_Base_AvailableCaptureSamples( void ) +{ + // !!! FIXME: write me. + return 0; +} + +static +void S_Base_Capture( int samples, byte *data ) +{ + // !!! FIXME: write me. +} + +static +void S_Base_StopCapture( void ) +{ + // !!! FIXME: write me. +} + +static +void S_Base_MasterGain( float val ) +{ + // !!! FIXME: write me. +} +#endif + + + +/* +================= +S_Base_SoundList +================= +*/ +void S_Base_SoundList( void ) { + int i; + sfx_t *sfx; + int size, total; + char type[4][16]; + char mem[2][16]; + + strcpy(type[0], "16bit"); + strcpy(type[1], "adpcm"); + strcpy(type[2], "daub4"); + strcpy(type[3], "mulaw"); + strcpy(mem[0], "paged out"); + strcpy(mem[1], "resident "); + total = 0; + for (sfx=s_knownSfx, i=0 ; isoundLength; + total += size; + Com_Printf("%6i[%s] : %s[%s]\n", size, type[sfx->soundCompressionMethod], + sfx->soundName, mem[sfx->inMemory] ); + } + Com_Printf ("Total resident: %i\n", total); + S_DisplayFreeMemory(); +} + + + +void S_ChannelFree(channel_t *v) { + v->thesfx = NULL; + *(channel_t **)v = freelist; + freelist = (channel_t*)v; +} + +channel_t* S_ChannelMalloc( void ) { + channel_t *v; + if (freelist == NULL) { + return NULL; + } + v = freelist; + freelist = *(channel_t **)freelist; + v->allocTime = Com_Milliseconds(); + return v; +} + +void S_ChannelSetup( void ) { + channel_t *p, *q; + + // clear all the sounds so they don't + Com_Memset( s_channels, 0, sizeof( s_channels ) ); + + p = s_channels;; + q = p + MAX_CHANNELS; + while (--q > p) { + *(channel_t **)q = q-1; + } + + *(channel_t **)q = NULL; + freelist = p + MAX_CHANNELS - 1; + Com_DPrintf("Channel memory manager started\n"); +} + + + +// ======================================================================= +// Load a sound +// ======================================================================= + +/* +================ +return a hash value for the sfx name +================ +*/ +static long S_HashSFXName(const char *name) { + int i; + long hash; + char letter; + + hash = 0; + i = 0; + while (name[i] != '\0') { + letter = tolower(name[i]); + if (letter =='.') break; // don't include extension + if (letter =='\\') letter = '/'; // damn path names + hash+=(long)(letter)*(i+119); + i++; + } + hash &= (LOOP_HASH-1); + return hash; +} + +/* +================== +S_FindName + +Will allocate a new sfx if it isn't found +================== +*/ +static sfx_t *S_FindName( const char *name ) { + int i; + int hash; + + sfx_t *sfx; + + if (!name) { + Com_Error (ERR_FATAL, "S_FindName: NULL\n"); + } + if (!name[0]) { + Com_Error (ERR_FATAL, "S_FindName: empty name\n"); + } + + if (strlen(name) >= MAX_QPATH) { + Com_Error (ERR_FATAL, "Sound name too long: %s", name); + } + + hash = S_HashSFXName(name); + + sfx = sfxHash[hash]; + // see if already loaded + while (sfx) { + if (!Q_stricmp(sfx->soundName, name) ) { + return sfx; + } + sfx = sfx->next; + } + + // find a free sfx + for (i=0 ; i < s_numSfx ; i++) { + if (!s_knownSfx[i].soundName[0]) { + break; + } + } + + if (i == s_numSfx) { + if (s_numSfx == MAX_SFX) { + Com_Error (ERR_FATAL, "S_FindName: out of sfx_t"); + } + s_numSfx++; + } + + sfx = &s_knownSfx[i]; + Com_Memset (sfx, 0, sizeof(*sfx)); + strcpy (sfx->soundName, name); + + sfx->next = sfxHash[hash]; + sfxHash[hash] = sfx; + + return sfx; +} + +/* +================= +S_DefaultSound +================= +*/ +void S_DefaultSound( sfx_t *sfx ) { + + int i; + + sfx->soundLength = 512; + sfx->soundData = SND_malloc(); + sfx->soundData->next = NULL; + + + for ( i = 0 ; i < sfx->soundLength ; i++ ) { + sfx->soundData->sndChunk[i] = i; + } +} + +/* +=================== +S_DisableSounds + +Disables sounds until the next S_BeginRegistration. +This is called when the hunk is cleared and the sounds +are no longer valid. +=================== +*/ +void S_Base_DisableSounds( void ) { + S_Base_StopAllSounds(); + s_soundMuted = qtrue; +} + +/* +================== +S_RegisterSound + +Creates a default buzz sound if the file can't be loaded +================== +*/ +sfxHandle_t S_Base_RegisterSound( const char *name, qboolean compressed ) { + sfx_t *sfx; + + compressed = qfalse; + if (!s_soundStarted) { + return 0; + } + + if ( strlen( name ) >= MAX_QPATH ) { + Com_Printf( "Sound name exceeds MAX_QPATH\n" ); + return 0; + } + + sfx = S_FindName( name ); + if ( sfx->soundData ) { + if ( sfx->defaultSound ) { + Com_Printf( S_COLOR_YELLOW "WARNING: could not find %s - using default\n", sfx->soundName ); + return 0; + } + return sfx - s_knownSfx; + } + + sfx->inMemory = qfalse; + sfx->soundCompressed = compressed; + + S_memoryLoad(sfx); + + if ( sfx->defaultSound ) { + Com_Printf( S_COLOR_YELLOW "WARNING: could not find %s - using default\n", sfx->soundName ); + return 0; + } + + return sfx - s_knownSfx; +} + +/* +===================== +S_BeginRegistration + +===================== +*/ +void S_Base_BeginRegistration( void ) { + s_soundMuted = qfalse; // we can play again + + if (s_numSfx == 0) { + SND_setup(); + + s_numSfx = 0; + Com_Memset( s_knownSfx, 0, sizeof( s_knownSfx ) ); + Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH); + + S_Base_RegisterSound("sound/feedback/hit.wav", qfalse); // changed to a sound in baseq3 + } +} + +void S_memoryLoad(sfx_t *sfx) { + // load the sound file + if ( !S_LoadSound ( sfx ) ) { +// Com_Printf( S_COLOR_YELLOW "WARNING: couldn't load sound: %s\n", sfx->soundName ); + sfx->defaultSound = qtrue; + } + sfx->inMemory = qtrue; +} + +//============================================================================= + +/* +================= +S_SpatializeOrigin + +Used for spatializing s_channels +================= +*/ +void S_SpatializeOrigin (vec3_t origin, int master_vol, int *left_vol, int *right_vol) +{ + vec_t dot; + vec_t dist; + vec_t lscale, rscale, scale; + vec3_t source_vec; + vec3_t vec; + + const float dist_mult = SOUND_ATTENUATE; + + // calculate stereo seperation and distance attenuation + VectorSubtract(origin, listener_origin, source_vec); + + dist = VectorNormalize(source_vec); + dist -= SOUND_FULLVOLUME; + if (dist < 0) + dist = 0; // close enough to be at full volume + dist *= dist_mult; // different attenuation levels + + VectorRotate( source_vec, listener_axis, vec ); + + dot = -vec[1]; + + if (dma.channels == 1) + { // no attenuation = no spatialization + rscale = 1.0; + lscale = 1.0; + } + else + { + rscale = 0.5 * (1.0 + dot); + lscale = 0.5 * (1.0 - dot); + if ( rscale < 0 ) { + rscale = 0; + } + if ( lscale < 0 ) { + lscale = 0; + } + } + + // add in distance effect + scale = (1.0 - dist) * rscale; + *right_vol = (master_vol * scale); + if (*right_vol < 0) + *right_vol = 0; + + scale = (1.0 - dist) * lscale; + *left_vol = (master_vol * scale); + if (*left_vol < 0) + *left_vol = 0; +} + +// ======================================================================= +// Start a sound effect +// ======================================================================= + +/* +==================== +S_StartSound + +Validates the parms and ques the sound up +if pos is NULL, the sound will be dynamically sourced from the entity +Entchannel 0 will never override a playing sound +==================== +*/ +void S_Base_StartSound(vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfxHandle ) { + channel_t *ch; + sfx_t *sfx; + int i, oldest, chosen, time; + int inplay, allowed; + + if ( !s_soundStarted || s_soundMuted ) { + return; + } + + if ( !origin && ( entityNum < 0 || entityNum > MAX_GENTITIES ) ) { + Com_Error( ERR_DROP, "S_StartSound: bad entitynum %i", entityNum ); + } + + if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) { + Com_Printf( S_COLOR_YELLOW "S_StartSound: handle %i out of range\n", sfxHandle ); + return; + } + + sfx = &s_knownSfx[ sfxHandle ]; + + if (sfx->inMemory == qfalse) { + S_memoryLoad(sfx); + } + + if ( s_show->integer == 1 ) { + Com_Printf( "%i : %s\n", s_paintedtime, sfx->soundName ); + } + + time = Com_Milliseconds(); + +// Com_Printf("playing %s\n", sfx->soundName); + // pick a channel to play on + + allowed = 4; + if (entityNum == listener_number) { + allowed = 8; + } + + ch = s_channels; + inplay = 0; + for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) { + if (ch->entnum == entityNum && ch->thesfx == sfx) { + if (time - ch->allocTime < 50) { +// if (Cvar_VariableValue( "cg_showmiss" )) { +// Com_Printf("double sound start\n"); +// } + return; + } + inplay++; + } + } + + if (inplay>allowed) { + return; + } + + sfx->lastTimeUsed = time; + + ch = S_ChannelMalloc(); // entityNum, entchannel); + if (!ch) { + ch = s_channels; + + oldest = sfx->lastTimeUsed; + chosen = -1; + for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { + if (ch->entnum != listener_number && ch->entnum == entityNum && ch->allocTimeentchannel != CHAN_ANNOUNCER) { + oldest = ch->allocTime; + chosen = i; + } + } + if (chosen == -1) { + ch = s_channels; + for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { + if (ch->entnum != listener_number && ch->allocTimeentchannel != CHAN_ANNOUNCER) { + oldest = ch->allocTime; + chosen = i; + } + } + if (chosen == -1) { + ch = s_channels; + if (ch->entnum == listener_number) { + for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { + if (ch->allocTimeallocTime; + chosen = i; + } + } + } + if (chosen == -1) { + Com_Printf("dropping sound\n"); + return; + } + } + } + ch = &s_channels[chosen]; + ch->allocTime = sfx->lastTimeUsed; + } + + if (origin) { + VectorCopy (origin, ch->origin); + ch->fixed_origin = qtrue; + } else { + ch->fixed_origin = qfalse; + } + + ch->master_vol = 127; + ch->entnum = entityNum; + ch->thesfx = sfx; + ch->startSample = START_SAMPLE_IMMEDIATE; + ch->entchannel = entchannel; + ch->leftvol = ch->master_vol; // these will get calced at next spatialize + ch->rightvol = ch->master_vol; // unless the game isn't running + ch->doppler = qfalse; +} + + +/* +================== +S_StartLocalSound +================== +*/ +void S_Base_StartLocalSound( sfxHandle_t sfxHandle, int channelNum ) { + if ( !s_soundStarted || s_soundMuted ) { + return; + } + + if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) { + Com_Printf( S_COLOR_YELLOW "S_StartLocalSound: handle %i out of range\n", sfxHandle ); + return; + } + + S_Base_StartSound (NULL, listener_number, channelNum, sfxHandle ); +} + + +/* +================== +S_ClearSoundBuffer + +If we are about to perform file access, clear the buffer +so sound doesn't stutter. +================== +*/ +void S_Base_ClearSoundBuffer( void ) { + int clear; + + if (!s_soundStarted) + return; + + // stop looping sounds + Com_Memset(loopSounds, 0, MAX_GENTITIES*sizeof(loopSound_t)); + Com_Memset(loop_channels, 0, MAX_CHANNELS*sizeof(channel_t)); + numLoopChannels = 0; + + S_ChannelSetup(); + + Com_Memset(s_rawend, '\0', sizeof (s_rawend)); + + if (dma.samplebits == 8) + clear = 0x80; + else + clear = 0; + + SNDDMA_BeginPainting (); + if (dma.buffer) + Com_Memset(dma.buffer, clear, dma.samples * dma.samplebits/8); + SNDDMA_Submit (); +} + +/* +================== +S_StopAllSounds +================== +*/ +void S_Base_StopAllSounds(void) { + if ( !s_soundStarted ) { + return; + } + + // stop the background music + S_Base_StopBackgroundTrack(); + + S_Base_ClearSoundBuffer (); +} + +/* +============================================================== + +continuous looping sounds are added each frame + +============================================================== +*/ + +void S_Base_StopLoopingSound(int entityNum) { + loopSounds[entityNum].active = qfalse; +// loopSounds[entityNum].sfx = 0; + loopSounds[entityNum].kill = qfalse; +} + +/* +================== +S_ClearLoopingSounds + +================== +*/ +void S_Base_ClearLoopingSounds( qboolean killall ) { + int i; + for ( i = 0 ; i < MAX_GENTITIES ; i++) { + if (killall || loopSounds[i].kill == qtrue || (loopSounds[i].sfx && loopSounds[i].sfx->soundLength == 0)) { + loopSounds[i].kill = qfalse; + S_Base_StopLoopingSound(i); + } + } + numLoopChannels = 0; +} + +/* +================== +S_AddLoopingSound + +Called during entity generation for a frame +Include velocity in case I get around to doing doppler... +================== +*/ +void S_Base_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) { + sfx_t *sfx; + + if ( !s_soundStarted || s_soundMuted ) { + return; + } + + if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) { + Com_Printf( S_COLOR_YELLOW "S_AddLoopingSound: handle %i out of range\n", sfxHandle ); + return; + } + + sfx = &s_knownSfx[ sfxHandle ]; + + if (sfx->inMemory == qfalse) { + S_memoryLoad(sfx); + } + + if ( !sfx->soundLength ) { + Com_Error( ERR_DROP, "%s has length 0", sfx->soundName ); + } + + VectorCopy( origin, loopSounds[entityNum].origin ); + VectorCopy( velocity, loopSounds[entityNum].velocity ); + loopSounds[entityNum].active = qtrue; + loopSounds[entityNum].kill = qtrue; + loopSounds[entityNum].doppler = qfalse; + loopSounds[entityNum].oldDopplerScale = 1.0; + loopSounds[entityNum].dopplerScale = 1.0; + loopSounds[entityNum].sfx = sfx; + + if (s_doppler->integer && VectorLengthSquared(velocity)>0.0) { + vec3_t out; + float lena, lenb; + + loopSounds[entityNum].doppler = qtrue; + lena = DistanceSquared(loopSounds[listener_number].origin, loopSounds[entityNum].origin); + VectorAdd(loopSounds[entityNum].origin, loopSounds[entityNum].velocity, out); + lenb = DistanceSquared(loopSounds[listener_number].origin, out); + if ((loopSounds[entityNum].framenum+1) != cls.framecount) { + loopSounds[entityNum].oldDopplerScale = 1.0; + } else { + loopSounds[entityNum].oldDopplerScale = loopSounds[entityNum].dopplerScale; + } + loopSounds[entityNum].dopplerScale = lenb/(lena*100); + if (loopSounds[entityNum].dopplerScale<=1.0) { + loopSounds[entityNum].doppler = qfalse; // don't bother doing the math + } else if (loopSounds[entityNum].dopplerScale>MAX_DOPPLER_SCALE) { + loopSounds[entityNum].dopplerScale = MAX_DOPPLER_SCALE; + } + } + + loopSounds[entityNum].framenum = cls.framecount; +} + +/* +================== +S_AddLoopingSound + +Called during entity generation for a frame +Include velocity in case I get around to doing doppler... +================== +*/ +void S_Base_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) { + sfx_t *sfx; + + if ( !s_soundStarted || s_soundMuted ) { + return; + } + + if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) { + Com_Printf( S_COLOR_YELLOW "S_AddRealLoopingSound: handle %i out of range\n", sfxHandle ); + return; + } + + sfx = &s_knownSfx[ sfxHandle ]; + + if (sfx->inMemory == qfalse) { + S_memoryLoad(sfx); + } + + if ( !sfx->soundLength ) { + Com_Error( ERR_DROP, "%s has length 0", sfx->soundName ); + } + VectorCopy( origin, loopSounds[entityNum].origin ); + VectorCopy( velocity, loopSounds[entityNum].velocity ); + loopSounds[entityNum].sfx = sfx; + loopSounds[entityNum].active = qtrue; + loopSounds[entityNum].kill = qfalse; + loopSounds[entityNum].doppler = qfalse; +} + + + +/* +================== +S_AddLoopSounds + +Spatialize all of the looping sounds. +All sounds are on the same cycle, so any duplicates can just +sum up the channel multipliers. +================== +*/ +void S_AddLoopSounds (void) { + int i, j, time; + int left_total, right_total, left, right; + channel_t *ch; + loopSound_t *loop, *loop2; + static int loopFrame; + + + numLoopChannels = 0; + + time = Com_Milliseconds(); + + loopFrame++; + for ( i = 0 ; i < MAX_GENTITIES ; i++) { + loop = &loopSounds[i]; + if ( !loop->active || loop->mergeFrame == loopFrame ) { + continue; // already merged into an earlier sound + } + + if (loop->kill) { + S_SpatializeOrigin( loop->origin, 127, &left_total, &right_total); // 3d + } else { + S_SpatializeOrigin( loop->origin, 90, &left_total, &right_total); // sphere + } + + loop->sfx->lastTimeUsed = time; + + for (j=(i+1); j< MAX_GENTITIES ; j++) { + loop2 = &loopSounds[j]; + if ( !loop2->active || loop2->doppler || loop2->sfx != loop->sfx) { + continue; + } + loop2->mergeFrame = loopFrame; + + if (loop2->kill) { + S_SpatializeOrigin( loop2->origin, 127, &left, &right); // 3d + } else { + S_SpatializeOrigin( loop2->origin, 90, &left, &right); // sphere + } + + loop2->sfx->lastTimeUsed = time; + left_total += left; + right_total += right; + } + if (left_total == 0 && right_total == 0) { + continue; // not audible + } + + // allocate a channel + ch = &loop_channels[numLoopChannels]; + + if (left_total > 255) { + left_total = 255; + } + if (right_total > 255) { + right_total = 255; + } + + ch->master_vol = 127; + ch->leftvol = left_total; + ch->rightvol = right_total; + ch->thesfx = loop->sfx; + ch->doppler = loop->doppler; + ch->dopplerScale = loop->dopplerScale; + ch->oldDopplerScale = loop->oldDopplerScale; + numLoopChannels++; + if (numLoopChannels == MAX_CHANNELS) { + return; + } + } +} + +//============================================================================= + +/* +================= +S_ByteSwapRawSamples + +If raw data has been loaded in little endien binary form, this must be done. +If raw data was calculated, as with ADPCM, this should not be called. +================= +*/ +void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *data ) { + int i; + + if ( width != 2 ) { + return; + } + if ( LittleShort( 256 ) == 256 ) { + return; + } + + if ( s_channels == 2 ) { + samples <<= 1; + } + for ( i = 0 ; i < samples ; i++ ) { + ((short *)data)[i] = LittleShort( ((short *)data)[i] ); + } +} + +/* +============ +S_RawSamples + +Music streaming +============ +*/ +void S_Base_RawSamples( int stream, int samples, int rate, int width, int s_channels, const byte *data, float volume ) { + int i; + int src, dst; + float scale; + int intVolume; + portable_samplepair_t *rawsamples; + + if ( !s_soundStarted || s_soundMuted ) { + return; + } + + if ( (stream < 0) || (stream >= MAX_RAW_STREAMS) ) { + return; + } + rawsamples = s_rawsamples[stream]; + + intVolume = 256 * volume; + + if ( s_rawend[stream] < s_soundtime ) { + Com_DPrintf( "S_RawSamples: resetting minimum: %i < %i\n", s_rawend[stream], s_soundtime ); + s_rawend[stream] = s_soundtime; + } + + scale = (float)rate / dma.speed; + +//Com_Printf ("%i < %i < %i\n", s_soundtime, s_paintedtime, s_rawend[stream]); + if (s_channels == 2 && width == 2) + { + if (scale == 1.0) + { // optimized case + for (i=0 ; i= samples) + break; + dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1); + s_rawend[stream]++; + rawsamples[dst].left = ((short *)data)[src*2] * intVolume; + rawsamples[dst].right = ((short *)data)[src*2+1] * intVolume; + } + } + } + else if (s_channels == 1 && width == 2) + { + for (i=0 ; ; i++) + { + src = i*scale; + if (src >= samples) + break; + dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1); + s_rawend[stream]++; + rawsamples[dst].left = ((short *)data)[src] * intVolume; + rawsamples[dst].right = ((short *)data)[src] * intVolume; + } + } + else if (s_channels == 2 && width == 1) + { + intVolume *= 256; + + for (i=0 ; ; i++) + { + src = i*scale; + if (src >= samples) + break; + dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1); + s_rawend[stream]++; + rawsamples[dst].left = ((char *)data)[src*2] * intVolume; + rawsamples[dst].right = ((char *)data)[src*2+1] * intVolume; + } + } + else if (s_channels == 1 && width == 1) + { + intVolume *= 256; + + for (i=0 ; ; i++) + { + src = i*scale; + if (src >= samples) + break; + dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1); + s_rawend[stream]++; + rawsamples[dst].left = (((byte *)data)[src]-128) * intVolume; + rawsamples[dst].right = (((byte *)data)[src]-128) * intVolume; + } + } + + if ( s_rawend[stream] > s_soundtime + MAX_RAW_SAMPLES ) { + Com_DPrintf( "S_RawSamples: overflowed %i > %i\n", s_rawend[stream], s_soundtime ); + } +} + +//============================================================================= + +/* +===================== +S_UpdateEntityPosition + +let the sound system know where an entity currently is +====================== +*/ +void S_Base_UpdateEntityPosition( int entityNum, const vec3_t origin ) { + if ( entityNum < 0 || entityNum > MAX_GENTITIES ) { + Com_Error( ERR_DROP, "S_UpdateEntityPosition: bad entitynum %i", entityNum ); + } + VectorCopy( origin, loopSounds[entityNum].origin ); +} + + +/* +============ +S_Respatialize + +Change the volumes of all the playing sounds for changes in their positions +============ +*/ +void S_Base_Respatialize( int entityNum, const vec3_t head, vec3_t axis[3], int inwater ) { + int i; + channel_t *ch; + vec3_t origin; + + if ( !s_soundStarted || s_soundMuted ) { + return; + } + + listener_number = entityNum; + VectorCopy(head, listener_origin); + VectorCopy(axis[0], listener_axis[0]); + VectorCopy(axis[1], listener_axis[1]); + VectorCopy(axis[2], listener_axis[2]); + + // update spatialization for dynamic sounds + ch = s_channels; + for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { + if ( !ch->thesfx ) { + continue; + } + // anything coming from the view entity will always be full volume + if (ch->entnum == listener_number) { + ch->leftvol = ch->master_vol; + ch->rightvol = ch->master_vol; + } else { + if (ch->fixed_origin) { + VectorCopy( ch->origin, origin ); + } else { + VectorCopy( loopSounds[ ch->entnum ].origin, origin ); + } + + S_SpatializeOrigin (origin, ch->master_vol, &ch->leftvol, &ch->rightvol); + } + } + + // add loopsounds + S_AddLoopSounds (); +} + + +/* +======================== +S_ScanChannelStarts + +Returns qtrue if any new sounds were started since the last mix +======================== +*/ +qboolean S_ScanChannelStarts( void ) { + channel_t *ch; + int i; + qboolean newSamples; + + newSamples = qfalse; + ch = s_channels; + + for (i=0; ithesfx ) { + continue; + } + // if this channel was just started this frame, + // set the sample count to it begins mixing + // into the very first sample + if ( ch->startSample == START_SAMPLE_IMMEDIATE ) { + ch->startSample = s_paintedtime; + newSamples = qtrue; + continue; + } + + // if it is completely finished by now, clear it + if ( ch->startSample + (ch->thesfx->soundLength) <= s_paintedtime ) { + S_ChannelFree(ch); + } + } + + return newSamples; +} + +/* +============ +S_Update + +Called once each time through the main loop +============ +*/ +void S_Base_Update( void ) { + int i; + int total; + channel_t *ch; + + if ( !s_soundStarted || s_soundMuted ) { +// Com_DPrintf ("not started or muted\n"); + return; + } + + // + // debugging output + // + if ( s_show->integer == 2 ) { + total = 0; + ch = s_channels; + for (i=0 ; ithesfx && (ch->leftvol || ch->rightvol) ) { + Com_Printf ("%d %d %s\n", ch->leftvol, ch->rightvol, ch->thesfx->soundName); + total++; + } + } + + Com_Printf ("----(%i)---- painted: %i\n", total, s_paintedtime); + } + + // add raw data from streamed samples + S_UpdateBackgroundTrack(); + + // mix some sound + S_Update_(); +} + +void S_GetSoundtime(void) +{ + int samplepos; + static int buffers; + static int oldsamplepos; + int fullsamples; + + fullsamples = dma.samples / dma.channels; + + if( CL_VideoRecording( ) ) + { + s_soundtime += (int)ceil( dma.speed / cl_aviFrameRate->value ); + return; + } + + // it is possible to miscount buffers if it has wrapped twice between + // calls to S_Update. Oh well. + samplepos = SNDDMA_GetDMAPos(); + if (samplepos < oldsamplepos) + { + buffers++; // buffer wrapped + + if (s_paintedtime > 0x40000000) + { // time to chop things off to avoid 32 bit limits + buffers = 0; + s_paintedtime = fullsamples; + S_Base_StopAllSounds (); + } + } + oldsamplepos = samplepos; + + s_soundtime = buffers*fullsamples + samplepos/dma.channels; + +#if 0 +// check to make sure that we haven't overshot + if (s_paintedtime < s_soundtime) + { + Com_DPrintf ("S_Update_ : overflow\n"); + s_paintedtime = s_soundtime; + } +#endif + + if ( dma.submission_chunk < 256 ) { + s_paintedtime = s_soundtime + s_mixPreStep->value * dma.speed; + } else { + s_paintedtime = s_soundtime + dma.submission_chunk; + } +} + + +void S_Update_(void) { + unsigned endtime; + int samps; + static float lastTime = 0.0f; + float ma, op; + float thisTime, sane; + static int ot = -1; + + if ( !s_soundStarted || s_soundMuted ) { + return; + } + + thisTime = Com_Milliseconds(); + + // Updates s_soundtime + S_GetSoundtime(); + + if (s_soundtime == ot) { + return; + } + ot = s_soundtime; + + // clear any sound effects that end before the current time, + // and start any new sounds + S_ScanChannelStarts(); + + sane = thisTime - lastTime; + if (sane<11) { + sane = 11; // 85hz + } + + ma = s_mixahead->value * dma.speed; + op = s_mixPreStep->value + sane*dma.speed*0.01; + + if (op < ma) { + ma = op; + } + + // mix ahead of current position + endtime = s_soundtime + ma; + + // mix to an even submission block size + endtime = (endtime + dma.submission_chunk-1) + & ~(dma.submission_chunk-1); + + // never mix more than the complete buffer + samps = dma.samples >> (dma.channels-1); + if (endtime - s_soundtime > samps) + endtime = s_soundtime + samps; + + + + SNDDMA_BeginPainting (); + + S_PaintChannels (endtime); + + SNDDMA_Submit (); + + lastTime = thisTime; +} + + + +/* +=============================================================================== + +background music functions + +=============================================================================== +*/ + +/* +====================== +S_StopBackgroundTrack +====================== +*/ +void S_Base_StopBackgroundTrack( void ) { + if(!s_backgroundStream) + return; + S_CodecCloseStream(s_backgroundStream); + s_backgroundStream = NULL; + s_rawend[0] = 0; +} + +/* +====================== +S_StartBackgroundTrack +====================== +*/ +void S_Base_StartBackgroundTrack( const char *intro, const char *loop ){ + if ( !intro ) { + intro = ""; + } + if ( !loop || !loop[0] ) { + loop = intro; + } + Com_DPrintf( "S_StartBackgroundTrack( %s, %s )\n", intro, loop ); + + if ( !intro[0] ) { + return; + } + + if( !loop ) { + s_backgroundLoop[0] = 0; + } else { + Q_strncpyz( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) ); + } + + // close the background track, but DON'T reset s_rawend + // if restarting the same back ground track + if(s_backgroundStream) + { + S_CodecCloseStream(s_backgroundStream); + s_backgroundStream = NULL; + } + + // Open stream + s_backgroundStream = S_CodecOpenStream(intro); + if(!s_backgroundStream) { + Com_Printf( S_COLOR_YELLOW "WARNING: couldn't open music file %s\n", intro ); + return; + } + + if(s_backgroundStream->info.channels != 2 || s_backgroundStream->info.rate != 22050) { + Com_Printf(S_COLOR_YELLOW "WARNING: music file %s is not 22k stereo\n", intro ); + } +} + +/* +====================== +S_UpdateBackgroundTrack +====================== +*/ +void S_UpdateBackgroundTrack( void ) { + int bufferSamples; + int fileSamples; + byte raw[30000]; // just enough to fit in a mac stack frame + int fileBytes; + int r; + static float musicVolume = 0.5f; + + if(!s_backgroundStream) { + return; + } + + // graeme see if this is OK + musicVolume = (musicVolume + (s_musicVolume->value * 2))/4.0f; + + // don't bother playing anything if musicvolume is 0 + if ( musicVolume <= 0 ) { + return; + } + + // see how many samples should be copied into the raw buffer + if ( s_rawend[0] < s_soundtime ) { + s_rawend[0] = s_soundtime; + } + + while ( s_rawend[0] < s_soundtime + MAX_RAW_SAMPLES ) { + bufferSamples = MAX_RAW_SAMPLES - (s_rawend[0] - s_soundtime); + + // decide how much data needs to be read from the file + fileSamples = bufferSamples * s_backgroundStream->info.rate / dma.speed; + + // our max buffer size + fileBytes = fileSamples * (s_backgroundStream->info.width * s_backgroundStream->info.channels); + if ( fileBytes > sizeof(raw) ) { + fileBytes = sizeof(raw); + fileSamples = fileBytes / (s_backgroundStream->info.width * s_backgroundStream->info.channels); + } + + // Read + r = S_CodecReadStream(s_backgroundStream, fileBytes, raw); + if(r < fileBytes) + { + fileBytes = r; + fileSamples = r / (s_backgroundStream->info.width * s_backgroundStream->info.channels); + } + + if(r > 0) + { + // add to raw buffer + S_Base_RawSamples( 0, fileSamples, s_backgroundStream->info.rate, + s_backgroundStream->info.width, s_backgroundStream->info.channels, raw, musicVolume ); + } + else + { + // loop + if(s_backgroundLoop[0]) + { + S_CodecCloseStream(s_backgroundStream); + s_backgroundStream = NULL; + S_Base_StartBackgroundTrack( s_backgroundLoop, s_backgroundLoop ); + if(!s_backgroundStream) + return; + } + else + { + S_Base_StopBackgroundTrack(); + return; + } + } + + } +} + + +/* +====================== +S_FreeOldestSound +====================== +*/ + +void S_FreeOldestSound( void ) { + int i, oldest, used; + sfx_t *sfx; + sndBuffer *buffer, *nbuffer; + + oldest = Com_Milliseconds(); + used = 0; + + for (i=1 ; i < s_numSfx ; i++) { + sfx = &s_knownSfx[i]; + if (sfx->inMemory && sfx->lastTimeUsedlastTimeUsed; + } + } + + sfx = &s_knownSfx[used]; + + Com_DPrintf("S_FreeOldestSound: freeing sound %s\n", sfx->soundName); + + buffer = sfx->soundData; + while(buffer != NULL) { + nbuffer = buffer->next; + SND_free(buffer); + buffer = nbuffer; + } + sfx->inMemory = qfalse; + sfx->soundData = NULL; +} + +// ======================================================================= +// Shutdown sound engine +// ======================================================================= + +void S_Base_Shutdown( void ) { + if ( !s_soundStarted ) { + return; + } + + SNDDMA_Shutdown(); + + s_soundStarted = 0; + + Cmd_RemoveCommand("s_info"); +} + +/* +================ +S_Init +================ +*/ +qboolean S_Base_Init( soundInterface_t *si ) { + qboolean r; + + if( !si ) { + return qfalse; + } + + s_khz = Cvar_Get ("s_khz", "22", CVAR_ARCHIVE); + s_mixahead = Cvar_Get ("s_mixahead", "0.2", CVAR_ARCHIVE); + s_mixPreStep = Cvar_Get ("s_mixPreStep", "0.05", CVAR_ARCHIVE); + s_show = Cvar_Get ("s_show", "0", CVAR_CHEAT); + s_testsound = Cvar_Get ("s_testsound", "0", CVAR_CHEAT); + + r = SNDDMA_Init(); + + if ( r ) { + s_soundStarted = 1; + s_soundMuted = 1; +// s_numSfx = 0; + + Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH); + + s_soundtime = 0; + s_paintedtime = 0; + + S_Base_StopAllSounds( ); + } else { + return qfalse; + } + + si->Shutdown = S_Base_Shutdown; + si->StartSound = S_Base_StartSound; + si->StartLocalSound = S_Base_StartLocalSound; + si->StartBackgroundTrack = S_Base_StartBackgroundTrack; + si->StopBackgroundTrack = S_Base_StopBackgroundTrack; + si->RawSamples = S_Base_RawSamples; + si->StopAllSounds = S_Base_StopAllSounds; + si->ClearLoopingSounds = S_Base_ClearLoopingSounds; + si->AddLoopingSound = S_Base_AddLoopingSound; + si->AddRealLoopingSound = S_Base_AddRealLoopingSound; + si->StopLoopingSound = S_Base_StopLoopingSound; + si->Respatialize = S_Base_Respatialize; + si->UpdateEntityPosition = S_Base_UpdateEntityPosition; + si->Update = S_Base_Update; + si->DisableSounds = S_Base_DisableSounds; + si->BeginRegistration = S_Base_BeginRegistration; + si->RegisterSound = S_Base_RegisterSound; + si->ClearSoundBuffer = S_Base_ClearSoundBuffer; + si->SoundInfo = S_Base_SoundInfo; + si->SoundList = S_Base_SoundList; + +#ifdef USE_VOIP + si->StartCapture = S_Base_StartCapture; + si->AvailableCaptureSamples = S_Base_AvailableCaptureSamples; + si->Capture = S_Base_Capture; + si->StopCapture = S_Base_StopCapture; + si->MasterGain = S_Base_MasterGain; +#endif + + return qtrue; +} diff --git a/reaction/engine/code/client/snd_local.h b/reaction/engine/code/client/snd_local.h new file mode 100644 index 00000000..86c7d4a2 --- /dev/null +++ b/reaction/engine/code/client/snd_local.h @@ -0,0 +1,250 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// snd_local.h -- private sound definations + + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "snd_public.h" + +#define PAINTBUFFER_SIZE 4096 // this is in samples + +#define SND_CHUNK_SIZE 1024 // samples +#define SND_CHUNK_SIZE_FLOAT (SND_CHUNK_SIZE/2) // floats +#define SND_CHUNK_SIZE_BYTE (SND_CHUNK_SIZE*2) // floats + +typedef struct { + int left; // the final values will be clamped to +/- 0x00ffff00 and shifted down + int right; +} portable_samplepair_t; + +typedef struct adpcm_state { + short sample; /* Previous output value */ + char index; /* Index into stepsize table */ +} adpcm_state_t; + +typedef struct sndBuffer_s { + short sndChunk[SND_CHUNK_SIZE]; + struct sndBuffer_s *next; + int size; + adpcm_state_t adpcm; +} sndBuffer; + +typedef struct sfx_s { + sndBuffer *soundData; + qboolean defaultSound; // couldn't be loaded, so use buzz + qboolean inMemory; // not in Memory + qboolean soundCompressed; // not in Memory + int soundCompressionMethod; + int soundLength; + char soundName[MAX_QPATH]; + int lastTimeUsed; + struct sfx_s *next; +} sfx_t; + +typedef struct { + int channels; + int samples; // mono samples in buffer + int submission_chunk; // don't mix less than this # + int samplebits; + int speed; + byte *buffer; +} dma_t; + +#define START_SAMPLE_IMMEDIATE 0x7fffffff + +#define MAX_DOPPLER_SCALE 50.0f //arbitrary + +typedef struct loopSound_s { + vec3_t origin; + vec3_t velocity; + sfx_t *sfx; + int mergeFrame; + qboolean active; + qboolean kill; + qboolean doppler; + float dopplerScale; + float oldDopplerScale; + int framenum; +} loopSound_t; + +typedef struct +{ + int allocTime; + int startSample; // START_SAMPLE_IMMEDIATE = set immediately on next mix + int entnum; // to allow overriding a specific sound + int entchannel; // to allow overriding a specific sound + int leftvol; // 0-255 volume after spatialization + int rightvol; // 0-255 volume after spatialization + int master_vol; // 0-255 volume before spatialization + float dopplerScale; + float oldDopplerScale; + vec3_t origin; // only use if fixed_origin is set + qboolean fixed_origin; // use origin instead of fetching entnum's origin + sfx_t *thesfx; // sfx structure + qboolean doppler; +} channel_t; + + +#define WAV_FORMAT_PCM 1 + + +typedef struct { + int format; + int rate; + int width; + int channels; + int samples; + int dataofs; // chunk starts this many bytes from file start +} wavinfo_t; + +// Interface between Q3 sound "api" and the sound backend +typedef struct +{ + void (*Shutdown)(void); + void (*StartSound)( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx ); + void (*StartLocalSound)( sfxHandle_t sfx, int channelNum ); + void (*StartBackgroundTrack)( const char *intro, const char *loop ); + void (*StopBackgroundTrack)( void ); + void (*RawSamples)(int stream, int samples, int rate, int width, int channels, const byte *data, float volume); + void (*StopAllSounds)( void ); + void (*ClearLoopingSounds)( qboolean killall ); + void (*AddLoopingSound)( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); + void (*AddRealLoopingSound)( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); + void (*StopLoopingSound)(int entityNum ); + void (*Respatialize)( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ); + void (*UpdateEntityPosition)( int entityNum, const vec3_t origin ); + void (*Update)( void ); + void (*DisableSounds)( void ); + void (*BeginRegistration)( void ); + sfxHandle_t (*RegisterSound)( const char *sample, qboolean compressed ); + void (*ClearSoundBuffer)( void ); + void (*SoundInfo)( void ); + void (*SoundList)( void ); +#ifdef USE_VOIP + void (*StartCapture)( void ); + int (*AvailableCaptureSamples)( void ); + void (*Capture)( int samples, byte *data ); + void (*StopCapture)( void ); + void (*MasterGain)( float gain ); +#endif +} soundInterface_t; + + +/* +==================================================================== + + SYSTEM SPECIFIC FUNCTIONS + +==================================================================== +*/ + +// initializes cycling through a DMA buffer and returns information on it +qboolean SNDDMA_Init(void); + +// gets the current DMA position +int SNDDMA_GetDMAPos(void); + +// shutdown the DMA xfer. +void SNDDMA_Shutdown(void); + +void SNDDMA_BeginPainting (void); + +void SNDDMA_Submit(void); + +//==================================================================== + +#define MAX_CHANNELS 96 + +extern channel_t s_channels[MAX_CHANNELS]; +extern channel_t loop_channels[MAX_CHANNELS]; +extern int numLoopChannels; + +extern int s_paintedtime; +extern vec3_t listener_forward; +extern vec3_t listener_right; +extern vec3_t listener_up; +extern dma_t dma; + +#define MAX_RAW_SAMPLES 16384 +#define MAX_RAW_STREAMS 128 +extern portable_samplepair_t s_rawsamples[MAX_RAW_STREAMS][MAX_RAW_SAMPLES]; +extern int s_rawend[MAX_RAW_STREAMS]; + +extern cvar_t *s_volume; +extern cvar_t *s_musicVolume; +extern cvar_t *s_doppler; + +extern cvar_t *s_testsound; + +qboolean S_LoadSound( sfx_t *sfx ); + +void SND_free(sndBuffer *v); +sndBuffer* SND_malloc( void ); +void SND_setup( void ); + +void S_PaintChannels(int endtime); + +void S_memoryLoad(sfx_t *sfx); + +// spatializes a channel +void S_Spatialize(channel_t *ch); + +// adpcm functions +int S_AdpcmMemoryNeeded( const wavinfo_t *info ); +void S_AdpcmEncodeSound( sfx_t *sfx, short *samples ); +void S_AdpcmGetSamples(sndBuffer *chunk, short *to); + +// wavelet function + +#define SENTINEL_MULAW_ZERO_RUN 127 +#define SENTINEL_MULAW_FOUR_BIT_RUN 126 + +void S_FreeOldestSound( void ); + +#define NXStream byte + +void encodeWavelet(sfx_t *sfx, short *packets); +void decodeWavelet( sndBuffer *stream, short *packets); + +void encodeMuLaw( sfx_t *sfx, short *packets); +extern short mulawToShort[256]; + +extern short *sfxScratchBuffer; +extern sfx_t *sfxScratchPointer; +extern int sfxScratchIndex; + +qboolean S_Base_Init( soundInterface_t *si ); + +// OpenAL stuff +typedef enum +{ + SRCPRI_AMBIENT = 0, // Ambient sound effects + SRCPRI_ENTITY, // Entity sound effects + SRCPRI_ONESHOT, // One-shot sounds + SRCPRI_LOCAL, // Local sounds + SRCPRI_STREAM // Streams (music, cutscenes) +} alSrcPriority_t; + +typedef int srcHandle_t; + +qboolean S_AL_Init( soundInterface_t *si ); diff --git a/reaction/engine/code/client/snd_main.c b/reaction/engine/code/client/snd_main.c new file mode 100644 index 00000000..2f48117a --- /dev/null +++ b/reaction/engine/code/client/snd_main.c @@ -0,0 +1,516 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com) + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "client.h" +#include "snd_codec.h" +#include "snd_local.h" +#include "snd_public.h" + +cvar_t *s_volume; +cvar_t *s_musicVolume; +cvar_t *s_doppler; +cvar_t *s_backend; +cvar_t *s_muteWhenMinimized; + +static soundInterface_t si; + +/* +================= +S_ValidateInterface +================= +*/ +static qboolean S_ValidSoundInterface( soundInterface_t *si ) +{ + if( !si->Shutdown ) return qfalse; + if( !si->StartSound ) return qfalse; + if( !si->StartLocalSound ) return qfalse; + if( !si->StartBackgroundTrack ) return qfalse; + if( !si->StopBackgroundTrack ) return qfalse; + if( !si->RawSamples ) return qfalse; + if( !si->StopAllSounds ) return qfalse; + if( !si->ClearLoopingSounds ) return qfalse; + if( !si->AddLoopingSound ) return qfalse; + if( !si->AddRealLoopingSound ) return qfalse; + if( !si->StopLoopingSound ) return qfalse; + if( !si->Respatialize ) return qfalse; + if( !si->UpdateEntityPosition ) return qfalse; + if( !si->Update ) return qfalse; + if( !si->DisableSounds ) return qfalse; + if( !si->BeginRegistration ) return qfalse; + if( !si->RegisterSound ) return qfalse; + if( !si->ClearSoundBuffer ) return qfalse; + if( !si->SoundInfo ) return qfalse; + if( !si->SoundList ) return qfalse; + +#ifdef USE_VOIP + if( !si->StartCapture ) return qfalse; + if( !si->AvailableCaptureSamples ) return qfalse; + if( !si->Capture ) return qfalse; + if( !si->StopCapture ) return qfalse; + if( !si->MasterGain ) return qfalse; +#endif + + return qtrue; +} + +/* +================= +S_StartSound +================= +*/ +void S_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx ) +{ + if( si.StartSound ) { + si.StartSound( origin, entnum, entchannel, sfx ); + } +} + +/* +================= +S_StartLocalSound +================= +*/ +void S_StartLocalSound( sfxHandle_t sfx, int channelNum ) +{ + if( si.StartLocalSound ) { + si.StartLocalSound( sfx, channelNum ); + } +} + +/* +================= +S_StartBackgroundTrack +================= +*/ +void S_StartBackgroundTrack( const char *intro, const char *loop ) +{ + if( si.StartBackgroundTrack ) { + si.StartBackgroundTrack( intro, loop ); + } +} + +/* +================= +S_StopBackgroundTrack +================= +*/ +void S_StopBackgroundTrack( void ) +{ + if( si.StopBackgroundTrack ) { + si.StopBackgroundTrack( ); + } +} + +/* +================= +S_RawSamples +================= +*/ +void S_RawSamples (int stream, int samples, int rate, int width, int channels, + const byte *data, float volume) +{ + if( si.RawSamples ) { + si.RawSamples( stream, samples, rate, width, channels, data, volume ); + } +} + +/* +================= +S_StopAllSounds +================= +*/ +void S_StopAllSounds( void ) +{ + if( si.StopAllSounds ) { + si.StopAllSounds( ); + } +} + +/* +================= +S_ClearLoopingSounds +================= +*/ +void S_ClearLoopingSounds( qboolean killall ) +{ + if( si.ClearLoopingSounds ) { + si.ClearLoopingSounds( killall ); + } +} + +/* +================= +S_AddLoopingSound +================= +*/ +void S_AddLoopingSound( int entityNum, const vec3_t origin, + const vec3_t velocity, sfxHandle_t sfx ) +{ + if( si.AddLoopingSound ) { + si.AddLoopingSound( entityNum, origin, velocity, sfx ); + } +} + +/* +================= +S_AddRealLoopingSound +================= +*/ +void S_AddRealLoopingSound( int entityNum, const vec3_t origin, + const vec3_t velocity, sfxHandle_t sfx ) +{ + if( si.AddRealLoopingSound ) { + si.AddRealLoopingSound( entityNum, origin, velocity, sfx ); + } +} + +/* +================= +S_StopLoopingSound +================= +*/ +void S_StopLoopingSound( int entityNum ) +{ + if( si.StopLoopingSound ) { + si.StopLoopingSound( entityNum ); + } +} + +/* +================= +S_Respatialize +================= +*/ +void S_Respatialize( int entityNum, const vec3_t origin, + vec3_t axis[3], int inwater ) +{ + if( si.Respatialize ) { + si.Respatialize( entityNum, origin, axis, inwater ); + } +} + +/* +================= +S_UpdateEntityPosition +================= +*/ +void S_UpdateEntityPosition( int entityNum, const vec3_t origin ) +{ + if( si.UpdateEntityPosition ) { + si.UpdateEntityPosition( entityNum, origin ); + } +} + +/* +================= +S_Update +================= +*/ +void S_Update( void ) +{ + if( s_muteWhenMinimized->integer && com_minimized->integer ) { + S_StopAllSounds( ); + return; + } + + if( si.Update ) { + si.Update( ); + } +} + +/* +================= +S_DisableSounds +================= +*/ +void S_DisableSounds( void ) +{ + if( si.DisableSounds ) { + si.DisableSounds( ); + } +} + +/* +================= +S_BeginRegistration +================= +*/ +void S_BeginRegistration( void ) +{ + if( si.BeginRegistration ) { + si.BeginRegistration( ); + } +} + +/* +================= +S_RegisterSound +================= +*/ +sfxHandle_t S_RegisterSound( const char *sample, qboolean compressed ) +{ + if( si.RegisterSound ) { + return si.RegisterSound( sample, compressed ); + } else { + return 0; + } +} + +/* +================= +S_ClearSoundBuffer +================= +*/ +void S_ClearSoundBuffer( void ) +{ + if( si.ClearSoundBuffer ) { + si.ClearSoundBuffer( ); + } +} + +/* +================= +S_SoundInfo +================= +*/ +void S_SoundInfo( void ) +{ + if( si.SoundInfo ) { + si.SoundInfo( ); + } +} + +/* +================= +S_SoundList +================= +*/ +void S_SoundList( void ) +{ + if( si.SoundList ) { + si.SoundList( ); + } +} + + +#ifdef USE_VOIP +/* +================= +S_StartCapture +================= +*/ +void S_StartCapture( void ) +{ + if( si.StartCapture ) { + si.StartCapture( ); + } +} + +/* +================= +S_AvailableCaptureSamples +================= +*/ +int S_AvailableCaptureSamples( void ) +{ + if( si.AvailableCaptureSamples ) { + return si.AvailableCaptureSamples( ); + } + return 0; +} + +/* +================= +S_Capture +================= +*/ +void S_Capture( int samples, byte *data ) +{ + if( si.Capture ) { + si.Capture( samples, data ); + } +} + +/* +================= +S_StopCapture +================= +*/ +void S_StopCapture( void ) +{ + if( si.StopCapture ) { + si.StopCapture( ); + } +} + +/* +================= +S_MasterGain +================= +*/ +void S_MasterGain( float gain ) +{ + if( si.MasterGain ) { + si.MasterGain( gain ); + } +} +#endif + +//============================================================================= + +/* +================= +S_Play_f +================= +*/ +void S_Play_f( void ) { + int i; + sfxHandle_t h; + char name[256]; + + if( !si.RegisterSound || !si.StartLocalSound ) { + return; + } + + i = 1; + while ( i [loopfile]\n"); + return; + } + +} + +//============================================================================= + +/* +================= +S_Init +================= +*/ +void S_Init( void ) +{ + cvar_t *cv; + qboolean started = qfalse; + + Com_Printf( "------ Initializing Sound ------\n" ); + + s_volume = Cvar_Get( "s_volume", "0.8", CVAR_ARCHIVE ); + s_musicVolume = Cvar_Get( "s_musicvolume", "0.25", CVAR_ARCHIVE ); + s_doppler = Cvar_Get( "s_doppler", "1", CVAR_ARCHIVE ); + s_backend = Cvar_Get( "s_backend", "", CVAR_ROM ); + s_muteWhenMinimized = Cvar_Get( "s_muteWhenMinimized", "0", CVAR_ARCHIVE ); + + cv = Cvar_Get( "s_initsound", "1", 0 ); + if( !cv->integer ) { + Com_Printf( "Sound disabled.\n" ); + } else { + + S_CodecInit( ); + + Cmd_AddCommand( "play", S_Play_f ); + Cmd_AddCommand( "music", S_Music_f ); + Cmd_AddCommand( "s_list", S_SoundList ); + Cmd_AddCommand( "s_stop", S_StopAllSounds ); + Cmd_AddCommand( "s_info", S_SoundInfo ); + + cv = Cvar_Get( "s_useOpenAL", "1", CVAR_ARCHIVE ); + if( cv->integer ) { + //OpenAL + started = S_AL_Init( &si ); + Cvar_Set( "s_backend", "OpenAL" ); + } + + if( !started ) { + started = S_Base_Init( &si ); + Cvar_Set( "s_backend", "base" ); + } + + if( started ) { + if( !S_ValidSoundInterface( &si ) ) { + Com_Error( ERR_FATAL, "Sound interface invalid." ); + } + + S_SoundInfo( ); + Com_Printf( "Sound initialization successful.\n" ); + } else { + Com_Printf( "Sound initialization failed.\n" ); + } + } + + Com_Printf( "--------------------------------\n"); +} + +/* +================= +S_Shutdown +================= +*/ +void S_Shutdown( void ) +{ + if( si.Shutdown ) { + si.Shutdown( ); + } + + Com_Memset( &si, 0, sizeof( soundInterface_t ) ); + + Cmd_RemoveCommand( "play" ); + Cmd_RemoveCommand( "music"); + Cmd_RemoveCommand( "s_list" ); + Cmd_RemoveCommand( "s_stop" ); + Cmd_RemoveCommand( "s_info" ); + + S_CodecShutdown( ); +} + diff --git a/reaction/engine/code/client/snd_mem.c b/reaction/engine/code/client/snd_mem.c new file mode 100644 index 00000000..36b7b13a --- /dev/null +++ b/reaction/engine/code/client/snd_mem.c @@ -0,0 +1,265 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: snd_mem.c + * + * desc: sound caching + * + * $Archive: /MissionPack/code/client/snd_mem.c $ + * + *****************************************************************************/ + +#include "snd_local.h" +#include "snd_codec.h" + +#define DEF_COMSOUNDMEGS "32" + +/* +=============================================================================== + +memory management + +=============================================================================== +*/ + +static sndBuffer *buffer = NULL; +static sndBuffer *freelist = NULL; +static int inUse = 0; +static int totalInUse = 0; + +short *sfxScratchBuffer = NULL; +sfx_t *sfxScratchPointer = NULL; +int sfxScratchIndex = 0; + +void SND_free(sndBuffer *v) { + *(sndBuffer **)v = freelist; + freelist = (sndBuffer*)v; + inUse += sizeof(sndBuffer); +} + +sndBuffer* SND_malloc(void) { + sndBuffer *v; +redo: + if (freelist == NULL) { + S_FreeOldestSound(); + goto redo; + } + + inUse -= sizeof(sndBuffer); + totalInUse += sizeof(sndBuffer); + + v = freelist; + freelist = *(sndBuffer **)freelist; + v->next = NULL; + return v; +} + +void SND_setup(void) { + sndBuffer *p, *q; + cvar_t *cv; + int scs; + + cv = Cvar_Get( "com_soundMegs", DEF_COMSOUNDMEGS, CVAR_LATCH | CVAR_ARCHIVE ); + + scs = (cv->integer*1536); + + buffer = malloc(scs*sizeof(sndBuffer) ); + // allocate the stack based hunk allocator + sfxScratchBuffer = malloc(SND_CHUNK_SIZE * sizeof(short) * 4); //Hunk_Alloc(SND_CHUNK_SIZE * sizeof(short) * 4); + sfxScratchPointer = NULL; + + inUse = scs*sizeof(sndBuffer); + p = buffer;; + q = p + scs; + while (--q > p) + *(sndBuffer **)q = q-1; + + *(sndBuffer **)q = NULL; + freelist = p + scs - 1; + + Com_Printf("Sound memory manager started\n"); +} + +/* +================ +ResampleSfx + +resample / decimate to the current source rate +================ +*/ +static void ResampleSfx( sfx_t *sfx, int inrate, int inwidth, byte *data, qboolean compressed ) { + int outcount; + int srcsample; + float stepscale; + int i; + int sample, samplefrac, fracstep; + int part; + sndBuffer *chunk; + + stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2 + + outcount = sfx->soundLength / stepscale; + sfx->soundLength = outcount; + + samplefrac = 0; + fracstep = stepscale * 256; + chunk = sfx->soundData; + + for (i=0 ; i> 8; + samplefrac += fracstep; + if( inwidth == 2 ) { + sample = ( ((short *)data)[srcsample] ); + } else { + sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8; + } + part = (i&(SND_CHUNK_SIZE-1)); + if (part == 0) { + sndBuffer *newchunk; + newchunk = SND_malloc(); + if (chunk == NULL) { + sfx->soundData = newchunk; + } else { + chunk->next = newchunk; + } + chunk = newchunk; + } + + chunk->sndChunk[part] = sample; + } +} + +/* +================ +ResampleSfx + +resample / decimate to the current source rate +================ +*/ +static int ResampleSfxRaw( short *sfx, int inrate, int inwidth, int samples, byte *data ) { + int outcount; + int srcsample; + float stepscale; + int i; + int sample, samplefrac, fracstep; + + stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2 + + outcount = samples / stepscale; + + samplefrac = 0; + fracstep = stepscale * 256; + + for (i=0 ; i> 8; + samplefrac += fracstep; + if( inwidth == 2 ) { + sample = LittleShort ( ((short *)data)[srcsample] ); + } else { + sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8; + } + sfx[i] = sample; + } + return outcount; +} + +//============================================================================= + +/* +============== +S_LoadSound + +The filename may be different than sfx->name in the case +of a forced fallback of a player specific sound +============== +*/ +qboolean S_LoadSound( sfx_t *sfx ) +{ + byte *data; + short *samples; + snd_info_t info; +// int size; + + // player specific sounds are never directly loaded + if ( sfx->soundName[0] == '*') { + return qfalse; + } + + // load it in + data = S_CodecLoad(sfx->soundName, &info); + if(!data) + return qfalse; + + if ( info.width == 1 ) { + Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is a 8 bit wav file\n", sfx->soundName); + } + + if ( info.rate != 22050 ) { + Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is not a 22kHz wav file\n", sfx->soundName); + } + + samples = Hunk_AllocateTempMemory(info.samples * sizeof(short) * 2); + + sfx->lastTimeUsed = Com_Milliseconds()+1; + + // each of these compression schemes works just fine + // but the 16bit quality is much nicer and with a local + // install assured we can rely upon the sound memory + // manager to do the right thing for us and page + // sound in as needed + + if( sfx->soundCompressed == qtrue) { + sfx->soundCompressionMethod = 1; + sfx->soundData = NULL; + sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, data + info.dataofs ); + S_AdpcmEncodeSound(sfx, samples); +#if 0 + } else if (info.samples>(SND_CHUNK_SIZE*16) && info.width >1) { + sfx->soundCompressionMethod = 3; + sfx->soundData = NULL; + sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) ); + encodeMuLaw( sfx, samples); + } else if (info.samples>(SND_CHUNK_SIZE*6400) && info.width >1) { + sfx->soundCompressionMethod = 2; + sfx->soundData = NULL; + sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) ); + encodeWavelet( sfx, samples); +#endif + } else { + sfx->soundCompressionMethod = 0; + sfx->soundLength = info.samples; + sfx->soundData = NULL; + ResampleSfx( sfx, info.rate, info.width, data + info.dataofs, qfalse ); + } + + Hunk_FreeTempMemory(samples); + Z_Free(data); + + return qtrue; +} + +void S_DisplayFreeMemory(void) { + Com_Printf("%d bytes free sound buffer memory, %d total used\n", inUse, totalInUse); +} diff --git a/reaction/engine/code/client/snd_mix.c b/reaction/engine/code/client/snd_mix.c new file mode 100644 index 00000000..cf0a9992 --- /dev/null +++ b/reaction/engine/code/client/snd_mix.c @@ -0,0 +1,738 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// snd_mix.c -- portable code to mix sounds for snd_dma.c + +#include "client.h" +#include "snd_local.h" +#if idppc_altivec && !defined(MACOS_X) +#include +#endif + +static portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; +static int snd_vol; + +int* snd_p; +int snd_linear_count; +short* snd_out; + +#if !id386 // if configured not to use asm + +void S_WriteLinearBlastStereo16 (void) +{ + int i; + int val; + + for (i=0 ; i>8; + if (val > 0x7fff) + snd_out[i] = 0x7fff; + else if (val < -32768) + snd_out[i] = -32768; + else + snd_out[i] = val; + + val = snd_p[i+1]>>8; + if (val > 0x7fff) + snd_out[i+1] = 0x7fff; + else if (val < -32768) + snd_out[i+1] = -32768; + else + snd_out[i+1] = val; + } +} +#elif defined(__GNUC__) +// uses snd_mixa.s +void S_WriteLinearBlastStereo16 (void); +#else + +__declspec( naked ) void S_WriteLinearBlastStereo16 (void) +{ + __asm { + + push edi + push ebx + mov ecx,ds:dword ptr[snd_linear_count] + mov ebx,ds:dword ptr[snd_p] + mov edi,ds:dword ptr[snd_out] +LWLBLoopTop: + mov eax,ds:dword ptr[-8+ebx+ecx*4] + sar eax,8 + cmp eax,07FFFh + jg LClampHigh + cmp eax,0FFFF8000h + jnl LClampDone + mov eax,0FFFF8000h + jmp LClampDone +LClampHigh: + mov eax,07FFFh +LClampDone: + mov edx,ds:dword ptr[-4+ebx+ecx*4] + sar edx,8 + cmp edx,07FFFh + jg LClampHigh2 + cmp edx,0FFFF8000h + jnl LClampDone2 + mov edx,0FFFF8000h + jmp LClampDone2 +LClampHigh2: + mov edx,07FFFh +LClampDone2: + shl edx,16 + and eax,0FFFFh + or edx,eax + mov ds:dword ptr[-4+edi+ecx*2],edx + sub ecx,2 + jnz LWLBLoopTop + pop ebx + pop edi + ret + } +} + +#endif + +void S_TransferStereo16 (unsigned long *pbuf, int endtime) +{ + int lpos; + int ls_paintedtime; + + snd_p = (int *) paintbuffer; + ls_paintedtime = s_paintedtime; + + while (ls_paintedtime < endtime) + { + // handle recirculating buffer issues + lpos = ls_paintedtime & ((dma.samples>>1)-1); + + snd_out = (short *) pbuf + (lpos<<1); + + snd_linear_count = (dma.samples>>1) - lpos; + if (ls_paintedtime + snd_linear_count > endtime) + snd_linear_count = endtime - ls_paintedtime; + + snd_linear_count <<= 1; + + // write a linear blast of samples + S_WriteLinearBlastStereo16 (); + + snd_p += snd_linear_count; + ls_paintedtime += (snd_linear_count>>1); + + if( CL_VideoRecording( ) ) + CL_WriteAVIAudioFrame( (byte *)snd_out, snd_linear_count << 1 ); + } +} + +/* +=================== +S_TransferPaintBuffer + +=================== +*/ +void S_TransferPaintBuffer(int endtime) +{ + int out_idx; + int count; + int out_mask; + int *p; + int step; + int val; + unsigned long *pbuf; + + pbuf = (unsigned long *)dma.buffer; + + + if ( s_testsound->integer ) { + int i; + int count; + + // write a fixed sine wave + count = (endtime - s_paintedtime); + for (i=0 ; i> 8; + p+= step; + if (val > 0x7fff) + val = 0x7fff; + else if (val < -32768) + val = -32768; + out[out_idx] = val; + out_idx = (out_idx + 1) & out_mask; + } + } + else if (dma.samplebits == 8) + { + unsigned char *out = (unsigned char *) pbuf; + while (count--) + { + val = *p >> 8; + p+= step; + if (val > 0x7fff) + val = 0x7fff; + else if (val < -32768) + val = -32768; + out[out_idx] = (val>>8) + 128; + out_idx = (out_idx + 1) & out_mask; + } + } + } +} + + +/* +=============================================================================== + +CHANNEL MIXING + +=============================================================================== +*/ + +#if idppc_altivec +static void S_PaintChannelFrom16_altivec( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { + int data, aoff, boff; + int leftvol, rightvol; + int i, j; + portable_samplepair_t *samp; + sndBuffer *chunk; + short *samples; + float ooff, fdata, fdiv, fleftvol, frightvol; + + samp = &paintbuffer[ bufferOffset ]; + + if (ch->doppler) { + sampleOffset = sampleOffset*ch->oldDopplerScale; + } + + chunk = sc->soundData; + while (sampleOffset>=SND_CHUNK_SIZE) { + chunk = chunk->next; + sampleOffset -= SND_CHUNK_SIZE; + if (!chunk) { + chunk = sc->soundData; + } + } + + if (!ch->doppler || ch->dopplerScale==1.0f) { + vector signed short volume_vec; + vector unsigned int volume_shift; + int vectorCount, samplesLeft, chunkSamplesLeft; + leftvol = ch->leftvol*snd_vol; + rightvol = ch->rightvol*snd_vol; + samples = chunk->sndChunk; + ((short *)&volume_vec)[0] = leftvol; + ((short *)&volume_vec)[1] = leftvol; + ((short *)&volume_vec)[4] = leftvol; + ((short *)&volume_vec)[5] = leftvol; + ((short *)&volume_vec)[2] = rightvol; + ((short *)&volume_vec)[3] = rightvol; + ((short *)&volume_vec)[6] = rightvol; + ((short *)&volume_vec)[7] = rightvol; + volume_shift = vec_splat_u32(8); + i = 0; + + while(i < count) { + /* Try to align destination to 16-byte boundary */ + while(i < count && (((unsigned long)&samp[i] & 0x1f) || ((count-i) < 8) || ((SND_CHUNK_SIZE - sampleOffset) < 8))) { + data = samples[sampleOffset++]; + samp[i].left += (data * leftvol)>>8; + samp[i].right += (data * rightvol)>>8; + + if (sampleOffset == SND_CHUNK_SIZE) { + chunk = chunk->next; + samples = chunk->sndChunk; + sampleOffset = 0; + } + i++; + } + /* Destination is now aligned. Process as many 8-sample + chunks as we can before we run out of room from the current + sound chunk. We do 8 per loop to avoid extra source data reads. */ + samplesLeft = count - i; + chunkSamplesLeft = SND_CHUNK_SIZE - sampleOffset; + if(samplesLeft > chunkSamplesLeft) + samplesLeft = chunkSamplesLeft; + + vectorCount = samplesLeft / 8; + + if(vectorCount) + { + vector unsigned char tmp; + vector short s0, s1, sampleData0, sampleData1; + vector signed int merge0, merge1; + vector signed int d0, d1, d2, d3; + vector unsigned char samplePermute0 = + VECCONST_UINT8(0, 1, 4, 5, 0, 1, 4, 5, 2, 3, 6, 7, 2, 3, 6, 7); + vector unsigned char samplePermute1 = + VECCONST_UINT8(8, 9, 12, 13, 8, 9, 12, 13, 10, 11, 14, 15, 10, 11, 14, 15); + vector unsigned char loadPermute0, loadPermute1; + + // Rather than permute the vectors after we load them to do the sample + // replication and rearrangement, we permute the alignment vector so + // we do everything in one step below and avoid data shuffling. + tmp = vec_lvsl(0,&samples[sampleOffset]); + loadPermute0 = vec_perm(tmp,tmp,samplePermute0); + loadPermute1 = vec_perm(tmp,tmp,samplePermute1); + + s0 = *(vector short *)&samples[sampleOffset]; + while(vectorCount) + { + /* Load up source (16-bit) sample data */ + s1 = *(vector short *)&samples[sampleOffset+7]; + + /* Load up destination sample data */ + d0 = *(vector signed int *)&samp[i]; + d1 = *(vector signed int *)&samp[i+2]; + d2 = *(vector signed int *)&samp[i+4]; + d3 = *(vector signed int *)&samp[i+6]; + + sampleData0 = vec_perm(s0,s1,loadPermute0); + sampleData1 = vec_perm(s0,s1,loadPermute1); + + merge0 = vec_mule(sampleData0,volume_vec); + merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */ + + merge1 = vec_mulo(sampleData0,volume_vec); + merge1 = vec_sra(merge1,volume_shift); + + d0 = vec_add(merge0,d0); + d1 = vec_add(merge1,d1); + + merge0 = vec_mule(sampleData1,volume_vec); + merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */ + + merge1 = vec_mulo(sampleData1,volume_vec); + merge1 = vec_sra(merge1,volume_shift); + + d2 = vec_add(merge0,d2); + d3 = vec_add(merge1,d3); + + /* Store destination sample data */ + *(vector signed int *)&samp[i] = d0; + *(vector signed int *)&samp[i+2] = d1; + *(vector signed int *)&samp[i+4] = d2; + *(vector signed int *)&samp[i+6] = d3; + + i += 8; + vectorCount--; + s0 = s1; + sampleOffset += 8; + } + if (sampleOffset == SND_CHUNK_SIZE) { + chunk = chunk->next; + samples = chunk->sndChunk; + sampleOffset = 0; + } + } + } + } else { + fleftvol = ch->leftvol*snd_vol; + frightvol = ch->rightvol*snd_vol; + + ooff = sampleOffset; + samples = chunk->sndChunk; + + for ( i=0 ; idopplerScale; + boff = ooff; + fdata = 0; + for (j=aoff; jnext; + if (!chunk) { + chunk = sc->soundData; + } + samples = chunk->sndChunk; + ooff -= SND_CHUNK_SIZE; + } + fdata += samples[j&(SND_CHUNK_SIZE-1)]; + } + fdiv = 256 * (boff-aoff); + samp[i].left += (fdata * fleftvol)/fdiv; + samp[i].right += (fdata * frightvol)/fdiv; + } + } +} +#endif + +static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { + int data, aoff, boff; + int leftvol, rightvol; + int i, j; + portable_samplepair_t *samp; + sndBuffer *chunk; + short *samples; + float ooff, fdata, fdiv, fleftvol, frightvol; + + samp = &paintbuffer[ bufferOffset ]; + + if (ch->doppler) { + sampleOffset = sampleOffset*ch->oldDopplerScale; + } + + chunk = sc->soundData; + while (sampleOffset>=SND_CHUNK_SIZE) { + chunk = chunk->next; + sampleOffset -= SND_CHUNK_SIZE; + if (!chunk) { + chunk = sc->soundData; + } + } + + if (!ch->doppler || ch->dopplerScale==1.0f) { + leftvol = ch->leftvol*snd_vol; + rightvol = ch->rightvol*snd_vol; + samples = chunk->sndChunk; + for ( i=0 ; i>8; + samp[i].right += (data * rightvol)>>8; + + if (sampleOffset == SND_CHUNK_SIZE) { + chunk = chunk->next; + samples = chunk->sndChunk; + sampleOffset = 0; + } + } + } else { + fleftvol = ch->leftvol*snd_vol; + frightvol = ch->rightvol*snd_vol; + + ooff = sampleOffset; + samples = chunk->sndChunk; + + + + + for ( i=0 ; idopplerScale; + boff = ooff; + fdata = 0; + for (j=aoff; jnext; + if (!chunk) { + chunk = sc->soundData; + } + samples = chunk->sndChunk; + ooff -= SND_CHUNK_SIZE; + } + fdata += samples[j&(SND_CHUNK_SIZE-1)]; + } + fdiv = 256 * (boff-aoff); + samp[i].left += (fdata * fleftvol)/fdiv; + samp[i].right += (fdata * frightvol)/fdiv; + } + } +} + +static void S_PaintChannelFrom16( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { +#if idppc_altivec + if (com_altivec->integer) { + // must be in a seperate function or G3 systems will crash. + S_PaintChannelFrom16_altivec( ch, sc, count, sampleOffset, bufferOffset ); + return; + } +#endif + S_PaintChannelFrom16_scalar( ch, sc, count, sampleOffset, bufferOffset ); +} + +void S_PaintChannelFromWavelet( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { + int data; + int leftvol, rightvol; + int i; + portable_samplepair_t *samp; + sndBuffer *chunk; + short *samples; + + leftvol = ch->leftvol*snd_vol; + rightvol = ch->rightvol*snd_vol; + + i = 0; + samp = &paintbuffer[ bufferOffset ]; + chunk = sc->soundData; + while (sampleOffset>=(SND_CHUNK_SIZE_FLOAT*4)) { + chunk = chunk->next; + sampleOffset -= (SND_CHUNK_SIZE_FLOAT*4); + i++; + } + + if (i!=sfxScratchIndex || sfxScratchPointer != sc) { + S_AdpcmGetSamples( chunk, sfxScratchBuffer ); + sfxScratchIndex = i; + sfxScratchPointer = sc; + } + + samples = sfxScratchBuffer; + + for ( i=0 ; i>8; + samp[i].right += (data * rightvol)>>8; + + if (sampleOffset == SND_CHUNK_SIZE*2) { + chunk = chunk->next; + decodeWavelet(chunk, sfxScratchBuffer); + sfxScratchIndex++; + sampleOffset = 0; + } + } +} + +void S_PaintChannelFromADPCM( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { + int data; + int leftvol, rightvol; + int i; + portable_samplepair_t *samp; + sndBuffer *chunk; + short *samples; + + leftvol = ch->leftvol*snd_vol; + rightvol = ch->rightvol*snd_vol; + + i = 0; + samp = &paintbuffer[ bufferOffset ]; + chunk = sc->soundData; + + if (ch->doppler) { + sampleOffset = sampleOffset*ch->oldDopplerScale; + } + + while (sampleOffset>=(SND_CHUNK_SIZE*4)) { + chunk = chunk->next; + sampleOffset -= (SND_CHUNK_SIZE*4); + i++; + } + + if (i!=sfxScratchIndex || sfxScratchPointer != sc) { + S_AdpcmGetSamples( chunk, sfxScratchBuffer ); + sfxScratchIndex = i; + sfxScratchPointer = sc; + } + + samples = sfxScratchBuffer; + + for ( i=0 ; i>8; + samp[i].right += (data * rightvol)>>8; + + if (sampleOffset == SND_CHUNK_SIZE*4) { + chunk = chunk->next; + S_AdpcmGetSamples( chunk, sfxScratchBuffer); + sampleOffset = 0; + sfxScratchIndex++; + } + } +} + +void S_PaintChannelFromMuLaw( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { + int data; + int leftvol, rightvol; + int i; + portable_samplepair_t *samp; + sndBuffer *chunk; + byte *samples; + float ooff; + + leftvol = ch->leftvol*snd_vol; + rightvol = ch->rightvol*snd_vol; + + samp = &paintbuffer[ bufferOffset ]; + chunk = sc->soundData; + while (sampleOffset>=(SND_CHUNK_SIZE*2)) { + chunk = chunk->next; + sampleOffset -= (SND_CHUNK_SIZE*2); + if (!chunk) { + chunk = sc->soundData; + } + } + + if (!ch->doppler) { + samples = (byte *)chunk->sndChunk + sampleOffset; + for ( i=0 ; i>8; + samp[i].right += (data * rightvol)>>8; + samples++; + if (samples == (byte *)chunk->sndChunk+(SND_CHUNK_SIZE*2)) { + chunk = chunk->next; + samples = (byte *)chunk->sndChunk; + } + } + } else { + ooff = sampleOffset; + samples = (byte *)chunk->sndChunk; + for ( i=0 ; idopplerScale; + samp[i].left += (data * leftvol)>>8; + samp[i].right += (data * rightvol)>>8; + if (ooff >= SND_CHUNK_SIZE*2) { + chunk = chunk->next; + if (!chunk) { + chunk = sc->soundData; + } + samples = (byte *)chunk->sndChunk; + ooff = 0.0; + } + } + } +} + +/* +=================== +S_PaintChannels +=================== +*/ +void S_PaintChannels( int endtime ) { + int i; + int end; + int stream; + channel_t *ch; + sfx_t *sc; + int ltime, count; + int sampleOffset; + + snd_vol = s_volume->value*255; + +//Com_Printf ("%i to %i\n", s_paintedtime, endtime); + while ( s_paintedtime < endtime ) { + // if paintbuffer is smaller than DMA buffer + // we may need to fill it multiple times + end = endtime; + if ( endtime - s_paintedtime > PAINTBUFFER_SIZE ) { + end = s_paintedtime + PAINTBUFFER_SIZE; + } + + // clear the paint buffer and mix any raw samples... + Com_Memset(paintbuffer, 0, sizeof (paintbuffer)); + for (stream = 0; stream < MAX_RAW_STREAMS; stream++) { + if ( s_rawend[stream] >= s_paintedtime ) { + // copy from the streaming sound source + const portable_samplepair_t *rawsamples = s_rawsamples[stream]; + const int stop = (end < s_rawend[stream]) ? end : s_rawend[stream]; + for ( i = s_paintedtime ; i < stop ; i++ ) { + const int s = i&(MAX_RAW_SAMPLES-1); + paintbuffer[i-s_paintedtime].left += rawsamples[s].left; + paintbuffer[i-s_paintedtime].right += rawsamples[s].right; + } + } + } + + // paint in the channels. + ch = s_channels; + for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) { + if ( !ch->thesfx || (ch->leftvol<0.25 && ch->rightvol<0.25 )) { + continue; + } + + ltime = s_paintedtime; + sc = ch->thesfx; + + sampleOffset = ltime - ch->startSample; + count = end - ltime; + if ( sampleOffset + count > sc->soundLength ) { + count = sc->soundLength - sampleOffset; + } + + if ( count > 0 ) { + if( sc->soundCompressionMethod == 1) { + S_PaintChannelFromADPCM (ch, sc, count, sampleOffset, ltime - s_paintedtime); + } else if( sc->soundCompressionMethod == 2) { + S_PaintChannelFromWavelet (ch, sc, count, sampleOffset, ltime - s_paintedtime); + } else if( sc->soundCompressionMethod == 3) { + S_PaintChannelFromMuLaw (ch, sc, count, sampleOffset, ltime - s_paintedtime); + } else { + S_PaintChannelFrom16 (ch, sc, count, sampleOffset, ltime - s_paintedtime); + } + } + } + + // paint in the looped channels. + ch = loop_channels; + for ( i = 0; i < numLoopChannels ; i++, ch++ ) { + if ( !ch->thesfx || (!ch->leftvol && !ch->rightvol )) { + continue; + } + + ltime = s_paintedtime; + sc = ch->thesfx; + + if (sc->soundData==NULL || sc->soundLength==0) { + continue; + } + // we might have to make two passes if it + // is a looping sound effect and the end of + // the sample is hit + do { + sampleOffset = (ltime % sc->soundLength); + + count = end - ltime; + if ( sampleOffset + count > sc->soundLength ) { + count = sc->soundLength - sampleOffset; + } + + if ( count > 0 ) { + if( sc->soundCompressionMethod == 1) { + S_PaintChannelFromADPCM (ch, sc, count, sampleOffset, ltime - s_paintedtime); + } else if( sc->soundCompressionMethod == 2) { + S_PaintChannelFromWavelet (ch, sc, count, sampleOffset, ltime - s_paintedtime); + } else if( sc->soundCompressionMethod == 3) { + S_PaintChannelFromMuLaw (ch, sc, count, sampleOffset, ltime - s_paintedtime); + } else { + S_PaintChannelFrom16 (ch, sc, count, sampleOffset, ltime - s_paintedtime); + } + ltime += count; + } + } while ( ltime < end); + } + + // transfer out according to DMA format + S_TransferPaintBuffer( end ); + s_paintedtime = end; + } +} diff --git a/reaction/engine/code/client/snd_openal.c b/reaction/engine/code/client/snd_openal.c new file mode 100644 index 00000000..5087b726 --- /dev/null +++ b/reaction/engine/code/client/snd_openal.c @@ -0,0 +1,2152 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com) + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "snd_local.h" +#include "snd_codec.h" +#include "client.h" + +#ifdef USE_OPENAL + +#include "qal.h" + +// Console variables specific to OpenAL +cvar_t *s_alPrecache; +cvar_t *s_alGain; +cvar_t *s_alSources; +cvar_t *s_alDopplerFactor; +cvar_t *s_alDopplerSpeed; +cvar_t *s_alMinDistance; +cvar_t *s_alMaxDistance; +cvar_t *s_alRolloff; +cvar_t *s_alGraceDistance; +cvar_t *s_alDriver; +cvar_t *s_alDevice; +cvar_t *s_alAvailableDevices; + +/* +================= +S_AL_Format +================= +*/ +static +ALuint S_AL_Format(int width, int channels) +{ + ALuint format = AL_FORMAT_MONO16; + + // Work out format + if(width == 1) + { + if(channels == 1) + format = AL_FORMAT_MONO8; + else if(channels == 2) + format = AL_FORMAT_STEREO8; + } + else if(width == 2) + { + if(channels == 1) + format = AL_FORMAT_MONO16; + else if(channels == 2) + format = AL_FORMAT_STEREO16; + } + + return format; +} + +/* +================= +S_AL_ErrorMsg +================= +*/ +static const char *S_AL_ErrorMsg(ALenum error) +{ + switch(error) + { + case AL_NO_ERROR: + return "No error"; + case AL_INVALID_NAME: + return "Invalid name"; + case AL_INVALID_ENUM: + return "Invalid enumerator"; + case AL_INVALID_VALUE: + return "Invalid value"; + case AL_INVALID_OPERATION: + return "Invalid operation"; + case AL_OUT_OF_MEMORY: + return "Out of memory"; + default: + return "Unknown error"; + } +} + + +//=========================================================================== + + +typedef struct alSfx_s +{ + char filename[MAX_QPATH]; + ALuint buffer; // OpenAL buffer + qboolean isDefault; // Couldn't be loaded - use default FX + qboolean inMemory; // Sound is stored in memory + qboolean isLocked; // Sound is locked (can not be unloaded) + int lastUsedTime; // Time last used +} alSfx_t; + +static qboolean alBuffersInitialised = qfalse; + +// Sound effect storage, data structures +#define MAX_SFX 4096 +static alSfx_t knownSfx[MAX_SFX]; +static int numSfx = 0; + +static sfxHandle_t default_sfx; + +/* +================= +S_AL_BufferFindFree + +Find a free handle +================= +*/ +static sfxHandle_t S_AL_BufferFindFree( void ) +{ + int i; + + for(i = 0; i < MAX_SFX; i++) + { + // Got one + if(knownSfx[i].filename[0] == '\0') + { + if(i >= numSfx) + numSfx = i + 1; + return i; + } + } + + // Shit... + Com_Error(ERR_FATAL, "S_AL_BufferFindFree: No free sound handles"); + return -1; +} + +/* +================= +S_AL_BufferFind + +Find a sound effect if loaded, set up a handle otherwise +================= +*/ +static sfxHandle_t S_AL_BufferFind(const char *filename) +{ + // Look it up in the table + sfxHandle_t sfx = -1; + int i; + + for(i = 0; i < numSfx; i++) + { + if(!Q_stricmp(knownSfx[i].filename, filename)) + { + sfx = i; + break; + } + } + + // Not found in table? + if(sfx == -1) + { + alSfx_t *ptr; + + sfx = S_AL_BufferFindFree(); + + // Clear and copy the filename over + ptr = &knownSfx[sfx]; + memset(ptr, 0, sizeof(*ptr)); + strcpy(ptr->filename, filename); + } + + // Return the handle + return sfx; +} + +/* +================= +S_AL_BufferUseDefault +================= +*/ +static void S_AL_BufferUseDefault(sfxHandle_t sfx) +{ + if(sfx == default_sfx) + Com_Error(ERR_FATAL, "Can't load default sound effect %s\n", knownSfx[sfx].filename); + + Com_Printf( S_COLOR_YELLOW "WARNING: Using default sound for %s\n", knownSfx[sfx].filename); + knownSfx[sfx].isDefault = qtrue; + knownSfx[sfx].buffer = knownSfx[default_sfx].buffer; +} + +/* +================= +S_AL_BufferUnload +================= +*/ +static void S_AL_BufferUnload(sfxHandle_t sfx) +{ + ALenum error; + + if(knownSfx[sfx].filename[0] == '\0') + return; + + if(!knownSfx[sfx].inMemory) + return; + + // Delete it + qalDeleteBuffers(1, &knownSfx[sfx].buffer); + if((error = qalGetError()) != AL_NO_ERROR) + Com_Printf( S_COLOR_RED "ERROR: Can't delete sound buffer for %s\n", + knownSfx[sfx].filename); + + knownSfx[sfx].inMemory = qfalse; +} + +/* +================= +S_AL_BufferEvict +================= +*/ +static qboolean S_AL_BufferEvict( void ) +{ + int i, oldestBuffer = -1; + int oldestTime = Sys_Milliseconds( ); + + for( i = 0; i < numSfx; i++ ) + { + if( !knownSfx[ i ].filename[ 0 ] ) + continue; + + if( !knownSfx[ i ].inMemory ) + continue; + + if( knownSfx[ i ].lastUsedTime < oldestTime ) + { + oldestTime = knownSfx[ i ].lastUsedTime; + oldestBuffer = i; + } + } + + if( oldestBuffer >= 0 ) + { + S_AL_BufferUnload( oldestBuffer ); + return qtrue; + } + else + return qfalse; +} + +/* +================= +S_AL_BufferLoad +================= +*/ +static void S_AL_BufferLoad(sfxHandle_t sfx) +{ + ALenum error; + + void *data; + snd_info_t info; + ALuint format; + + // Nothing? + if(knownSfx[sfx].filename[0] == '\0') + return; + + // Player SFX + if(knownSfx[sfx].filename[0] == '*') + return; + + // Already done? + if((knownSfx[sfx].inMemory) || (knownSfx[sfx].isDefault)) + return; + + // Try to load + data = S_CodecLoad(knownSfx[sfx].filename, &info); + if(!data) + { + S_AL_BufferUseDefault(sfx); + return; + } + + format = S_AL_Format(info.width, info.channels); + + // Create a buffer + qalGenBuffers(1, &knownSfx[sfx].buffer); + if((error = qalGetError()) != AL_NO_ERROR) + { + S_AL_BufferUseDefault(sfx); + Z_Free(data); + Com_Printf( S_COLOR_RED "ERROR: Can't create a sound buffer for %s - %s\n", + knownSfx[sfx].filename, S_AL_ErrorMsg(error)); + return; + } + + // Fill the buffer + if( info.size == 0 ) + { + // We have no data to buffer, so buffer silence + byte dummyData[ 2 ] = { 0 }; + + qalBufferData(knownSfx[sfx].buffer, AL_FORMAT_MONO16, (void *)dummyData, 2, 22050); + } + else + qalBufferData(knownSfx[sfx].buffer, format, data, info.size, info.rate); + + error = qalGetError(); + + // If we ran out of memory, start evicting the least recently used sounds + while(error == AL_OUT_OF_MEMORY) + { + if( !S_AL_BufferEvict( ) ) + { + S_AL_BufferUseDefault(sfx); + Z_Free(data); + Com_Printf( S_COLOR_RED "ERROR: Out of memory loading %s\n", knownSfx[sfx].filename); + return; + } + + // Try load it again + qalBufferData(knownSfx[sfx].buffer, format, data, info.size, info.rate); + error = qalGetError(); + } + + // Some other error condition + if(error != AL_NO_ERROR) + { + S_AL_BufferUseDefault(sfx); + Z_Free(data); + Com_Printf( S_COLOR_RED "ERROR: Can't fill sound buffer for %s - %s\n", + knownSfx[sfx].filename, S_AL_ErrorMsg(error)); + return; + } + + // Free the memory + Z_Free(data); + + // Woo! + knownSfx[sfx].inMemory = qtrue; +} + +/* +================= +S_AL_BufferUse +================= +*/ +static +void S_AL_BufferUse(sfxHandle_t sfx) +{ + if(knownSfx[sfx].filename[0] == '\0') + return; + + if((!knownSfx[sfx].inMemory) && (!knownSfx[sfx].isDefault)) + S_AL_BufferLoad(sfx); + knownSfx[sfx].lastUsedTime = Sys_Milliseconds(); +} + +/* +================= +S_AL_BufferInit +================= +*/ +static +qboolean S_AL_BufferInit( void ) +{ + if(alBuffersInitialised) + return qtrue; + + // Clear the hash table, and SFX table + memset(knownSfx, 0, sizeof(knownSfx)); + numSfx = 0; + + // Load the default sound, and lock it + default_sfx = S_AL_BufferFind("sound/feedback/hit.wav"); + S_AL_BufferUse(default_sfx); + knownSfx[default_sfx].isLocked = qtrue; + + // All done + alBuffersInitialised = qtrue; + return qtrue; +} + +/* +================= +S_AL_BufferShutdown +================= +*/ +static +void S_AL_BufferShutdown( void ) +{ + int i; + + if(!alBuffersInitialised) + return; + + // Unlock the default sound effect + knownSfx[default_sfx].isLocked = qfalse; + + // Free all used effects + for(i = 0; i < numSfx; i++) + S_AL_BufferUnload(i); + + // Clear the tables + memset(knownSfx, 0, sizeof(knownSfx)); + + // All undone + alBuffersInitialised = qfalse; +} + +/* +================= +S_AL_RegisterSound +================= +*/ +static +sfxHandle_t S_AL_RegisterSound( const char *sample, qboolean compressed ) +{ + sfxHandle_t sfx = S_AL_BufferFind(sample); + + if( s_alPrecache->integer && (!knownSfx[sfx].inMemory) && (!knownSfx[sfx].isDefault)) + S_AL_BufferLoad(sfx); + knownSfx[sfx].lastUsedTime = Com_Milliseconds(); + + return sfx; +} + +/* +================= +S_AL_BufferGet + +Return's an sfx's buffer +================= +*/ +static +ALuint S_AL_BufferGet(sfxHandle_t sfx) +{ + return knownSfx[sfx].buffer; +} + + +//=========================================================================== + + +typedef struct src_s +{ + ALuint alSource; // OpenAL source object + sfxHandle_t sfx; // Sound effect in use + + int lastUsedTime; // Last time used + alSrcPriority_t priority; // Priority + int entity; // Owning entity (-1 if none) + int channel; // Associated channel (-1 if none) + + int isActive; // Is this source currently in use? + int isLocked; // This is locked (un-allocatable) + int isLooping; // Is this a looping effect (attached to an entity) + int isTracking; // Is this object tracking it's owner + + float curGain; // gain employed if source is within maxdistance. + float scaleGain; // Last gain value for this source. 0 if muted. + + qboolean local; // Is this local (relative to the cam) +} src_t; + +#ifdef MACOS_X + #define MAX_SRC 64 +#else + #define MAX_SRC 128 +#endif +static src_t srcList[MAX_SRC]; +static int srcCount = 0; +static qboolean alSourcesInitialised = qfalse; +static vec3_t lastListenerOrigin = { 0.0f, 0.0f, 0.0f }; + +typedef struct sentity_s +{ + vec3_t origin; + + int srcAllocated; // If a src_t has been allocated to this entity + int srcIndex; + + qboolean loopAddedThisFrame; + alSrcPriority_t loopPriority; + sfxHandle_t loopSfx; + qboolean startLoopingSound; +} sentity_t; + +static sentity_t entityList[MAX_GENTITIES]; + +/* +================= +S_AL_SanitiseVector +================= +*/ +#define S_AL_SanitiseVector(v) _S_AL_SanitiseVector(v,__LINE__) +static void _S_AL_SanitiseVector( vec3_t v, int line ) +{ + if( Q_isnan( v[ 0 ] ) || Q_isnan( v[ 1 ] ) || Q_isnan( v[ 2 ] ) ) + { + Com_DPrintf( S_COLOR_YELLOW "WARNING: vector with one or more NaN components " + "being passed to OpenAL at %s:%d -- zeroing\n", __FILE__, line ); + VectorClear( v ); + } +} + + +#define AL_THIRD_PERSON_THRESHOLD_SQ (48.0f*48.0f) + +/* +================= +S_AL_ScaleGain +Adapt the gain if necessary to get a quicker fadeout when the source is too far away. +================= +*/ + +static void S_AL_ScaleGain(src_t *chksrc, vec3_t origin) +{ + float distance; + + if(!chksrc->local) + distance = Distance(origin, lastListenerOrigin); + + // If we exceed a certain distance, scale the gain linearly until the sound + // vanishes into nothingness. + if(!chksrc->local && (distance -= s_alMaxDistance->value) > 0) + { + float scaleFactor; + + if(distance >= s_alGraceDistance->value) + scaleFactor = 0.0f; + else + scaleFactor = 1.0f - distance / s_alGraceDistance->value; + + scaleFactor *= chksrc->curGain; + + if(chksrc->scaleGain != scaleFactor); + { + chksrc->scaleGain = scaleFactor; + // if(scaleFactor > 0.0f) + // Com_Printf("%f\n", scaleFactor); + qalSourcef(chksrc->alSource, AL_GAIN, chksrc->scaleGain); + } + } + else if(chksrc->scaleGain != chksrc->curGain) + { + chksrc->scaleGain = chksrc->curGain; + qalSourcef(chksrc->alSource, AL_GAIN, chksrc->scaleGain); + } +} + +/* +================= +S_AL_HearingThroughEntity +================= +*/ +static qboolean S_AL_HearingThroughEntity( int entityNum ) +{ + float distanceSq; + + if( clc.clientNum == entityNum ) + { + // FIXME: 28/02/06 This is an outrageous hack to detect + // whether or not the player is rendering in third person or not. We can't + // ask the renderer because the renderer has no notion of entities and we + // can't ask cgame since that would involve changing the API and hence mod + // compatibility. I don't think there is any way around this, but I'll leave + // the FIXME just in case anyone has a bright idea. + distanceSq = DistanceSquared( + entityList[ entityNum ].origin, + lastListenerOrigin ); + + if( distanceSq > AL_THIRD_PERSON_THRESHOLD_SQ ) + return qfalse; //we're the player, but third person + else + return qtrue; //we're the player + } + else + return qfalse; //not the player +} + +/* +================= +S_AL_SrcInit +================= +*/ +static +qboolean S_AL_SrcInit( void ) +{ + int i; + int limit; + ALenum error; + + // Clear the sources data structure + memset(srcList, 0, sizeof(srcList)); + srcCount = 0; + + // Cap s_alSources to MAX_SRC + limit = s_alSources->integer; + if(limit > MAX_SRC) + limit = MAX_SRC; + else if(limit < 16) + limit = 16; + + // Allocate as many sources as possible + for(i = 0; i < limit; i++) + { + qalGenSources(1, &srcList[i].alSource); + if((error = qalGetError()) != AL_NO_ERROR) + break; + srcCount++; + } + + // All done. Print this for informational purposes + Com_Printf( "Allocated %d sources.\n", srcCount); + alSourcesInitialised = qtrue; + return qtrue; +} + +/* +================= +S_AL_SrcShutdown +================= +*/ +static +void S_AL_SrcShutdown( void ) +{ + int i; + + if(!alSourcesInitialised) + return; + + // Destroy all the sources + for(i = 0; i < srcCount; i++) + { + if(srcList[i].isLocked) + Com_DPrintf( S_COLOR_YELLOW "WARNING: Source %d is locked\n", i); + + qalSourceStop(srcList[i].alSource); + qalDeleteSources(1, &srcList[i].alSource); + } + + memset(srcList, 0, sizeof(srcList)); + + alSourcesInitialised = qfalse; +} + +/* +================= +S_AL_SrcSetup +================= +*/ +static void S_AL_SrcSetup(srcHandle_t src, sfxHandle_t sfx, alSrcPriority_t priority, + int entity, int channel, qboolean local) +{ + ALuint buffer; + src_t *curSource; + + // Mark the SFX as used, and grab the raw AL buffer + S_AL_BufferUse(sfx); + buffer = S_AL_BufferGet(sfx); + + // Set up src struct + curSource = &srcList[src]; + + curSource->lastUsedTime = Sys_Milliseconds(); + curSource->sfx = sfx; + curSource->priority = priority; + curSource->entity = entity; + curSource->channel = channel; + curSource->isActive = qtrue; + curSource->isLocked = qfalse; + curSource->isLooping = qfalse; + curSource->isTracking = qfalse; + curSource->curGain = s_alGain->value * s_volume->value; + curSource->scaleGain = curSource->curGain; + curSource->local = local; + + // Set up OpenAL source + qalSourcei(curSource->alSource, AL_BUFFER, buffer); + qalSourcef(curSource->alSource, AL_PITCH, 1.0f); + qalSourcef(curSource->alSource, AL_GAIN, curSource->curGain); + qalSourcefv(curSource->alSource, AL_POSITION, vec3_origin); + qalSourcefv(curSource->alSource, AL_VELOCITY, vec3_origin); + qalSourcei(curSource->alSource, AL_LOOPING, AL_FALSE); + qalSourcef(curSource->alSource, AL_REFERENCE_DISTANCE, s_alMinDistance->value); + + if(local) + { + qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_TRUE); + qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, 0.0f); + } + else + { + qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_FALSE); + qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value); + } +} + +/* +================= +S_AL_SrcKill +================= +*/ +static void S_AL_SrcKill(srcHandle_t src) +{ + // I'm not touching it. Unlock it first. + if(srcList[src].isLocked) + return; + + // Stop it if it's playing + if(srcList[src].isActive) + qalSourceStop(srcList[src].alSource); + + // Remove the entity association + if((srcList[src].isLooping) && (srcList[src].entity != -1)) + { + int ent = srcList[src].entity; + entityList[ent].srcAllocated = qfalse; + entityList[ent].srcIndex = -1; + entityList[ent].loopAddedThisFrame = qfalse; + entityList[ent].startLoopingSound = qfalse; + } + + // Remove the buffer + qalSourcei(srcList[src].alSource, AL_BUFFER, 0); + + srcList[src].sfx = 0; + srcList[src].lastUsedTime = 0; + srcList[src].priority = 0; + srcList[src].entity = -1; + srcList[src].channel = -1; + srcList[src].isActive = qfalse; + srcList[src].isLocked = qfalse; + srcList[src].isLooping = qfalse; + srcList[src].isTracking = qfalse; + srcList[src].local = qfalse; +} + +/* +================= +S_AL_SrcAlloc +================= +*/ +static +srcHandle_t S_AL_SrcAlloc( alSrcPriority_t priority, int entnum, int channel ) +{ + int i; + int empty = -1; + int weakest = -1; + int weakest_time = Sys_Milliseconds(); + int weakest_pri = 999; + + for(i = 0; i < srcCount; i++) + { + // If it's locked, we aren't even going to look at it + if(srcList[i].isLocked) + continue; + + // Is it empty or not? + if((!srcList[i].isActive) && (empty == -1)) + empty = i; + else if(srcList[i].priority < priority) + { + // If it's older or has lower priority, flag it as weak + if((srcList[i].priority < weakest_pri) || + (srcList[i].lastUsedTime < weakest_time)) + { + weakest_pri = srcList[i].priority; + weakest_time = srcList[i].lastUsedTime; + weakest = i; + } + } + + // The channel system is not actually adhered to by baseq3, and not + // implemented in snd_dma.c, so while the following is strictly correct, it + // causes incorrect behaviour versus defacto baseq3 +#if 0 + // Is it an exact match, and not on channel 0? + if((srcList[i].entity == entnum) && (srcList[i].channel == channel) && (channel != 0)) + { + S_AL_SrcKill(i); + return i; + } +#endif + } + + // Do we have an empty one? + if(empty != -1) + { + S_AL_SrcKill( empty ); + return empty; + } + + // No. How about an overridable one? + if(weakest != -1) + { + S_AL_SrcKill(weakest); + return weakest; + } + + // Nothing. Return failure (cries...) + return -1; +} + +/* +================= +S_AL_SrcFind + +Finds an active source with matching entity and channel numbers +Returns -1 if there isn't one +================= +*/ +#if 0 +static +srcHandle_t S_AL_SrcFind(int entnum, int channel) +{ + int i; + for(i = 0; i < srcCount; i++) + { + if(!srcList[i].isActive) + continue; + if((srcList[i].entity == entnum) && (srcList[i].channel == channel)) + return i; + } + return -1; +} +#endif + +/* +================= +S_AL_SrcLock + +Locked sources will not be automatically reallocated or managed +================= +*/ +static +void S_AL_SrcLock(srcHandle_t src) +{ + srcList[src].isLocked = qtrue; +} + +/* +================= +S_AL_SrcUnlock + +Once unlocked, the source may be reallocated again +================= +*/ +static +void S_AL_SrcUnlock(srcHandle_t src) +{ + srcList[src].isLocked = qfalse; +} + +/* +================= +S_AL_UpdateEntityPosition +================= +*/ +static +void S_AL_UpdateEntityPosition( int entityNum, const vec3_t origin ) +{ + vec3_t sanOrigin; + + VectorCopy( origin, sanOrigin ); + S_AL_SanitiseVector( sanOrigin ); + if ( entityNum < 0 || entityNum > MAX_GENTITIES ) + Com_Error( ERR_DROP, "S_UpdateEntityPosition: bad entitynum %i", entityNum ); + VectorCopy( sanOrigin, entityList[entityNum].origin ); +} + +/* +================= +S_AL_CheckInput +Check whether input values from mods are out of range. +Necessary for i.g. Western Quake3 mod which is buggy. +================= +*/ +static qboolean S_AL_CheckInput(int entityNum, sfxHandle_t sfx) +{ + if (entityNum < 0 || entityNum > MAX_GENTITIES) + Com_Error(ERR_DROP, "S_StartSound: bad entitynum %i", entityNum); + + if (sfx < 0 || sfx >= numSfx) + { + Com_Printf(S_COLOR_RED "ERROR: S_AL_CheckInput: handle %i out of range\n", sfx); + return qtrue; + } + + return qfalse; +} + +/* +================= +S_AL_StartLocalSound + +Play a local (non-spatialized) sound effect +================= +*/ +static +void S_AL_StartLocalSound(sfxHandle_t sfx, int channel) +{ + srcHandle_t src; + + if(S_AL_CheckInput(0, sfx)) + return; + + // Try to grab a source + src = S_AL_SrcAlloc(SRCPRI_LOCAL, -1, channel); + + if(src == -1) + return; + + // Set up the effect + S_AL_SrcSetup(src, sfx, SRCPRI_LOCAL, -1, channel, qtrue); + + // Start it playing + qalSourcePlay(srcList[src].alSource); +} + +/* +================= +S_AL_StartSound + +Play a one-shot sound effect +================= +*/ +static +void S_AL_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx ) +{ + vec3_t sorigin; + srcHandle_t src; + + if(S_AL_CheckInput(origin ? 0 : entnum, sfx)) + return; + + // Try to grab a source + src = S_AL_SrcAlloc(SRCPRI_ONESHOT, entnum, entchannel); + if(src == -1) + return; + + // Set up the effect + if( origin == NULL ) + { + if( S_AL_HearingThroughEntity( entnum ) ) + { + // Where the entity is the local player, play a local sound + S_AL_SrcSetup( src, sfx, SRCPRI_ONESHOT, entnum, entchannel, qtrue ); + VectorClear( sorigin ); + } + else + { + S_AL_SrcSetup( src, sfx, SRCPRI_ONESHOT, entnum, entchannel, qfalse ); + VectorCopy( entityList[ entnum ].origin, sorigin ); + } + srcList[ src ].isTracking = qtrue; + } + else + { + S_AL_SrcSetup( src, sfx, SRCPRI_ONESHOT, entnum, entchannel, qfalse ); + VectorCopy( origin, sorigin ); + } + + S_AL_SanitiseVector( sorigin ); + qalSourcefv( srcList[ src ].alSource, AL_POSITION, sorigin ); + S_AL_ScaleGain(&srcList[src], sorigin); + + // Start it playing + qalSourcePlay(srcList[src].alSource); +} + +/* +================= +S_AL_ClearLoopingSounds +================= +*/ +static +void S_AL_ClearLoopingSounds( qboolean killall ) +{ + int i; + for(i = 0; i < srcCount; i++) + { + if((srcList[i].isLooping) && (srcList[i].entity != -1)) + entityList[srcList[i].entity].loopAddedThisFrame = qfalse; + } +} + +/* +================= +S_AL_SrcLoop +================= +*/ +static void S_AL_SrcLoop( alSrcPriority_t priority, sfxHandle_t sfx, + const vec3_t origin, const vec3_t velocity, int entityNum ) +{ + int src; + sentity_t *sent = &entityList[ entityNum ]; + src_t *curSource; + + // Do we need to allocate a new source for this entity + if( !sent->srcAllocated ) + { + // Try to get a channel + src = S_AL_SrcAlloc( priority, entityNum, -1 ); + if( src == -1 ) + { + Com_DPrintf( S_COLOR_YELLOW "WARNING: Failed to allocate source " + "for loop sfx %d on entity %d\n", sfx, entityNum ); + return; + } + + sent->startLoopingSound = qtrue; + } + else + src = sent->srcIndex; + + sent->srcAllocated = qtrue; + sent->srcIndex = src; + + sent->loopPriority = priority; + sent->loopSfx = sfx; + + // If this is not set then the looping sound is removed + sent->loopAddedThisFrame = qtrue; + + curSource = &srcList[src]; + + // UGH + // These lines should be called via S_AL_SrcSetup, but we + // can't call that yet as it buffers sfxes that may change + // with subsequent calls to S_AL_SrcLoop + curSource->entity = entityNum; + curSource->isLooping = qtrue; + curSource->isActive = qtrue; + + if( S_AL_HearingThroughEntity( entityNum ) ) + { + curSource->local = qtrue; + + qalSourcefv( curSource->alSource, AL_POSITION, vec3_origin ); + qalSourcefv( curSource->alSource, AL_VELOCITY, vec3_origin ); + } + else + { + curSource->local = qfalse; + + qalSourcefv( curSource->alSource, AL_POSITION, (ALfloat *)sent->origin ); + qalSourcefv( curSource->alSource, AL_VELOCITY, (ALfloat *)velocity ); + + } + + S_AL_ScaleGain(curSource, sent->origin); +} + +/* +================= +S_AL_AddLoopingSound +================= +*/ +static +void S_AL_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) +{ + vec3_t sanOrigin, sanVelocity; + + if(S_AL_CheckInput(entityNum, sfx)) + return; + + VectorCopy( origin, sanOrigin ); + VectorCopy( velocity, sanVelocity ); + S_AL_SanitiseVector( sanOrigin ); + S_AL_SanitiseVector( sanVelocity ); + + S_AL_SrcLoop(SRCPRI_ENTITY, sfx, sanOrigin, sanVelocity, entityNum); +} + +/* +================= +S_AL_AddRealLoopingSound +================= +*/ +static +void S_AL_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) +{ + vec3_t sanOrigin, sanVelocity; + + if(S_AL_CheckInput(entityNum, sfx)) + return; + + VectorCopy( origin, sanOrigin ); + VectorCopy( velocity, sanVelocity ); + S_AL_SanitiseVector( sanOrigin ); + S_AL_SanitiseVector( sanVelocity ); + + // There are certain maps (*cough* Q3:TA mpterra*) that have large quantities + // of ET_SPEAKERS in the PVS at any given time. OpenAL can't cope with mixing + // large numbers of sounds, so this culls them by distance + if( DistanceSquared( sanOrigin, lastListenerOrigin ) > (s_alMaxDistance->value + s_alGraceDistance->value) * + (s_alMaxDistance->value + s_alGraceDistance->value) ) + return; + + S_AL_SrcLoop(SRCPRI_AMBIENT, sfx, sanOrigin, sanVelocity, entityNum); +} + +/* +================= +S_AL_StopLoopingSound +================= +*/ +static +void S_AL_StopLoopingSound(int entityNum ) +{ + if(entityList[entityNum].srcAllocated) + S_AL_SrcKill(entityList[entityNum].srcIndex); +} + +/* +================= +S_AL_SrcUpdate + +Update state (move things around, manage sources, and so on) +================= +*/ +static +void S_AL_SrcUpdate( void ) +{ + int i; + int entityNum; + ALint state; + src_t *curSource; + + for(i = 0; i < srcCount; i++) + { + entityNum = srcList[i].entity; + curSource = &srcList[i]; + + if(curSource->isLocked) + continue; + + if(!curSource->isActive) + continue; + + // Update source parameters + if((s_alGain->modified)||(s_volume->modified)) + curSource->curGain = s_alGain->value * s_volume->value; + if((s_alRolloff->modified)&&(!curSource->local)) + qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value); + if(s_alMinDistance->modified) + qalSourcef(curSource->alSource, AL_REFERENCE_DISTANCE, s_alMinDistance->value); + + if(curSource->isLooping) + { + sentity_t *sent = &entityList[ entityNum ]; + + // If a looping effect hasn't been touched this frame, kill it + if(sent->loopAddedThisFrame) + { + // The sound has changed without an intervening removal + if(curSource->isActive && !sent->startLoopingSound && + curSource->sfx != sent->loopSfx) + { + qalSourceStop(curSource->alSource); + qalSourcei(curSource->alSource, AL_BUFFER, 0); + sent->startLoopingSound = qtrue; + } + + // The sound hasn't been started yet + if(sent->startLoopingSound) + { + S_AL_SrcSetup(i, sent->loopSfx, sent->loopPriority, + entityNum, -1, curSource->local); + curSource->isLooping = qtrue; + qalSourcei(curSource->alSource, AL_LOOPING, AL_TRUE); + qalSourcePlay(curSource->alSource); + + sent->startLoopingSound = qfalse; + } + + // Update locality + if(curSource->local) + { + qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_TRUE); + qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, 0.0f); + } + else + { + qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_FALSE); + qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value); + } + } + else + S_AL_SrcKill( i ); + + continue; + } + + // Check if it's done, and flag it + qalGetSourcei(curSource->alSource, AL_SOURCE_STATE, &state); + if(state == AL_STOPPED) + { + S_AL_SrcKill(i); + continue; + } + + // Query relativity of source, don't move if it's true + qalGetSourcei(curSource->alSource, AL_SOURCE_RELATIVE, &state); + + // See if it needs to be moved + if(curSource->isTracking && !state) + { + qalSourcefv(curSource->alSource, AL_POSITION, entityList[entityNum].origin); + S_AL_ScaleGain(curSource, entityList[entityNum].origin); + } + } +} + +/* +================= +S_AL_SrcShutup +================= +*/ +static +void S_AL_SrcShutup( void ) +{ + int i; + for(i = 0; i < srcCount; i++) + S_AL_SrcKill(i); +} + +/* +================= +S_AL_SrcGet +================= +*/ +static +ALuint S_AL_SrcGet(srcHandle_t src) +{ + return srcList[src].alSource; +} + + +//=========================================================================== + +static srcHandle_t streamSourceHandles[MAX_RAW_STREAMS]; +static qboolean streamPlaying[MAX_RAW_STREAMS]; +static ALuint streamSources[MAX_RAW_STREAMS]; + +/* +================= +S_AL_AllocateStreamChannel +================= +*/ +static void S_AL_AllocateStreamChannel( int stream ) +{ + if ((stream < 0) || (stream >= MAX_RAW_STREAMS)) + return; + + // Allocate a streamSource at high priority + streamSourceHandles[stream] = S_AL_SrcAlloc(SRCPRI_STREAM, -2, 0); + if(streamSourceHandles[stream] == -1) + return; + + // Lock the streamSource so nobody else can use it, and get the raw streamSource + S_AL_SrcLock(streamSourceHandles[stream]); + streamSources[stream] = S_AL_SrcGet(streamSourceHandles[stream]); + + // Set some streamSource parameters + qalSourcei (streamSources[stream], AL_BUFFER, 0 ); + qalSourcei (streamSources[stream], AL_LOOPING, AL_FALSE ); + qalSource3f(streamSources[stream], AL_POSITION, 0.0, 0.0, 0.0); + qalSource3f(streamSources[stream], AL_VELOCITY, 0.0, 0.0, 0.0); + qalSource3f(streamSources[stream], AL_DIRECTION, 0.0, 0.0, 0.0); + qalSourcef (streamSources[stream], AL_ROLLOFF_FACTOR, 0.0 ); + qalSourcei (streamSources[stream], AL_SOURCE_RELATIVE, AL_TRUE ); +} + +/* +================= +S_AL_FreeStreamChannel +================= +*/ +static void S_AL_FreeStreamChannel( int stream ) +{ + if ((stream < 0) || (stream >= MAX_RAW_STREAMS)) + return; + + // Release the output streamSource + S_AL_SrcUnlock(streamSourceHandles[stream]); + streamSources[stream] = 0; + streamSourceHandles[stream] = -1; +} + +/* +================= +S_AL_RawSamples +================= +*/ +static +void S_AL_RawSamples(int stream, int samples, int rate, int width, int channels, const byte *data, float volume) +{ + ALuint buffer; + ALuint format; + + if ((stream < 0) || (stream >= MAX_RAW_STREAMS)) + return; + + format = S_AL_Format( width, channels ); + + // Create the streamSource if necessary + if(streamSourceHandles[stream] == -1) + { + S_AL_AllocateStreamChannel(stream); + + // Failed? + if(streamSourceHandles[stream] == -1) + { + Com_Printf( S_COLOR_RED "ERROR: Can't allocate streaming streamSource\n"); + return; + } + } + + // Create a buffer, and stuff the data into it + qalGenBuffers(1, &buffer); + qalBufferData(buffer, format, (ALvoid *)data, (samples * width * channels), rate); + + // Shove the data onto the streamSource + qalSourceQueueBuffers(streamSources[stream], 1, &buffer); + + // Volume + qalSourcef (streamSources[stream], AL_GAIN, volume * s_volume->value * s_alGain->value); +} + +/* +================= +S_AL_StreamUpdate +================= +*/ +static +void S_AL_StreamUpdate( int stream ) +{ + int numBuffers; + ALint state; + + if ((stream < 0) || (stream >= MAX_RAW_STREAMS)) + return; + + if(streamSourceHandles[stream] == -1) + return; + + // Un-queue any buffers, and delete them + qalGetSourcei( streamSources[stream], AL_BUFFERS_PROCESSED, &numBuffers ); + while( numBuffers-- ) + { + ALuint buffer; + qalSourceUnqueueBuffers(streamSources[stream], 1, &buffer); + qalDeleteBuffers(1, &buffer); + } + + // Start the streamSource playing if necessary + qalGetSourcei( streamSources[stream], AL_BUFFERS_QUEUED, &numBuffers ); + + qalGetSourcei(streamSources[stream], AL_SOURCE_STATE, &state); + if(state == AL_STOPPED) + { + streamPlaying[stream] = qfalse; + + // If there are no buffers queued up, release the streamSource + if( !numBuffers ) + S_AL_FreeStreamChannel( stream ); + } + + if( !streamPlaying[stream] && numBuffers ) + { + qalSourcePlay( streamSources[stream] ); + streamPlaying[stream] = qtrue; + } +} + +/* +================= +S_AL_StreamDie +================= +*/ +static +void S_AL_StreamDie( int stream ) +{ + int numBuffers; + + if ((stream < 0) || (stream >= MAX_RAW_STREAMS)) + return; + + if(streamSourceHandles[stream] == -1) + return; + + streamPlaying[stream] = qfalse; + qalSourceStop(streamSources[stream]); + + // Un-queue any buffers, and delete them + qalGetSourcei( streamSources[stream], AL_BUFFERS_PROCESSED, &numBuffers ); + while( numBuffers-- ) + { + ALuint buffer; + qalSourceUnqueueBuffers(streamSources[stream], 1, &buffer); + qalDeleteBuffers(1, &buffer); + } + + S_AL_FreeStreamChannel(stream); +} + + +//=========================================================================== + + +#define NUM_MUSIC_BUFFERS 4 +#define MUSIC_BUFFER_SIZE 4096 + +static qboolean musicPlaying = qfalse; +static srcHandle_t musicSourceHandle = -1; +static ALuint musicSource; +static ALuint musicBuffers[NUM_MUSIC_BUFFERS]; + +static snd_stream_t *mus_stream; +static snd_stream_t *intro_stream; +static char s_backgroundLoop[MAX_QPATH]; + +static byte decode_buffer[MUSIC_BUFFER_SIZE]; + +/* +================= +S_AL_MusicSourceGet +================= +*/ +static void S_AL_MusicSourceGet( void ) +{ + // Allocate a musicSource at high priority + musicSourceHandle = S_AL_SrcAlloc(SRCPRI_STREAM, -2, 0); + if(musicSourceHandle == -1) + return; + + // Lock the musicSource so nobody else can use it, and get the raw musicSource + S_AL_SrcLock(musicSourceHandle); + musicSource = S_AL_SrcGet(musicSourceHandle); + + // Set some musicSource parameters + qalSource3f(musicSource, AL_POSITION, 0.0, 0.0, 0.0); + qalSource3f(musicSource, AL_VELOCITY, 0.0, 0.0, 0.0); + qalSource3f(musicSource, AL_DIRECTION, 0.0, 0.0, 0.0); + qalSourcef (musicSource, AL_ROLLOFF_FACTOR, 0.0 ); + qalSourcei (musicSource, AL_SOURCE_RELATIVE, AL_TRUE ); +} + +/* +================= +S_AL_MusicSourceFree +================= +*/ +static void S_AL_MusicSourceFree( void ) +{ + // Release the output musicSource + S_AL_SrcUnlock(musicSourceHandle); + musicSource = 0; + musicSourceHandle = -1; +} + +/* +================= +S_AL_CloseMusicFiles +================= +*/ +static void S_AL_CloseMusicFiles(void) +{ + if(intro_stream) + { + S_CodecCloseStream(intro_stream); + intro_stream = NULL; + } + + if(mus_stream) + { + S_CodecCloseStream(mus_stream); + mus_stream = NULL; + } +} + +/* +================= +S_AL_StopBackgroundTrack +================= +*/ +static +void S_AL_StopBackgroundTrack( void ) +{ + if(!musicPlaying) + return; + + // Stop playing + qalSourceStop(musicSource); + + // De-queue the musicBuffers + qalSourceUnqueueBuffers(musicSource, NUM_MUSIC_BUFFERS, musicBuffers); + + // Destroy the musicBuffers + qalDeleteBuffers(NUM_MUSIC_BUFFERS, musicBuffers); + + // Free the musicSource + S_AL_MusicSourceFree(); + + // Unload the stream + S_AL_CloseMusicFiles(); + + musicPlaying = qfalse; +} + +/* +================= +S_AL_MusicProcess +================= +*/ +static +void S_AL_MusicProcess(ALuint b) +{ + ALenum error; + int l; + ALuint format; + snd_stream_t *curstream; + + if(intro_stream) + curstream = intro_stream; + else + curstream = mus_stream; + + if(!curstream) + return; + + l = S_CodecReadStream(curstream, MUSIC_BUFFER_SIZE, decode_buffer); + + // Run out data to read, start at the beginning again + if(l == 0) + { + S_CodecCloseStream(curstream); + + // the intro stream just finished playing so we don't need to reopen + // the music stream. + if(intro_stream) + intro_stream = NULL; + else + mus_stream = S_CodecOpenStream(s_backgroundLoop); + + curstream = mus_stream; + + if(!curstream) + { + S_AL_StopBackgroundTrack(); + return; + } + + l = S_CodecReadStream(curstream, MUSIC_BUFFER_SIZE, decode_buffer); + } + + format = S_AL_Format(curstream->info.width, curstream->info.channels); + + if( l == 0 ) + { + // We have no data to buffer, so buffer silence + byte dummyData[ 2 ] = { 0 }; + + qalBufferData( b, AL_FORMAT_MONO16, (void *)dummyData, 2, 22050 ); + } + else + qalBufferData(b, format, decode_buffer, l, curstream->info.rate); + + if( ( error = qalGetError( ) ) != AL_NO_ERROR ) + { + S_AL_StopBackgroundTrack( ); + Com_Printf( S_COLOR_RED "ERROR: while buffering data for music stream - %s\n", + S_AL_ErrorMsg( error ) ); + return; + } +} + +/* +================= +S_AL_StartBackgroundTrack +================= +*/ +static +void S_AL_StartBackgroundTrack( const char *intro, const char *loop ) +{ + int i; + qboolean issame; + + // Stop any existing music that might be playing + S_AL_StopBackgroundTrack(); + + if((!intro || !*intro) && (!loop || !*loop)) + return; + + // Allocate a musicSource + S_AL_MusicSourceGet(); + if(musicSourceHandle == -1) + return; + + if (!loop || !*loop) + { + loop = intro; + issame = qtrue; + } + else if(intro && *intro && !strcmp(intro, loop)) + issame = qtrue; + else + issame = qfalse; + + // Copy the loop over + strncpy( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) ); + + if(!issame) + { + // Open the intro and don't mind whether it succeeds. + // The important part is the loop. + intro_stream = S_CodecOpenStream(intro); + } + else + intro_stream = NULL; + + mus_stream = S_CodecOpenStream(s_backgroundLoop); + if(!mus_stream) + { + S_AL_CloseMusicFiles(); + S_AL_MusicSourceFree(); + return; + } + + // Generate the musicBuffers + qalGenBuffers(NUM_MUSIC_BUFFERS, musicBuffers); + + // Queue the musicBuffers up + for(i = 0; i < NUM_MUSIC_BUFFERS; i++) + { + S_AL_MusicProcess(musicBuffers[i]); + } + + qalSourceQueueBuffers(musicSource, NUM_MUSIC_BUFFERS, musicBuffers); + + // Set the initial gain property + qalSourcef(musicSource, AL_GAIN, s_alGain->value * s_musicVolume->value); + + // Start playing + qalSourcePlay(musicSource); + + musicPlaying = qtrue; +} + +/* +================= +S_AL_MusicUpdate +================= +*/ +static +void S_AL_MusicUpdate( void ) +{ + int numBuffers; + ALint state; + + if(!musicPlaying) + return; + + qalGetSourcei( musicSource, AL_BUFFERS_PROCESSED, &numBuffers ); + while( numBuffers-- ) + { + ALuint b; + qalSourceUnqueueBuffers(musicSource, 1, &b); + S_AL_MusicProcess(b); + qalSourceQueueBuffers(musicSource, 1, &b); + } + + // Hitches can cause OpenAL to be starved of buffers when streaming. + // If this happens, it will stop playback. This restarts the source if + // it is no longer playing, and if there are buffers available + qalGetSourcei( musicSource, AL_SOURCE_STATE, &state ); + qalGetSourcei( musicSource, AL_BUFFERS_QUEUED, &numBuffers ); + if( state == AL_STOPPED && numBuffers ) + { + Com_DPrintf( S_COLOR_YELLOW "Restarted OpenAL music\n" ); + qalSourcePlay(musicSource); + } + + // Set the gain property + qalSourcef(musicSource, AL_GAIN, s_alGain->value * s_musicVolume->value); +} + + +//=========================================================================== + + +// Local state variables +static ALCdevice *alDevice; +static ALCcontext *alContext; + +#ifdef USE_VOIP +static ALCdevice *alCaptureDevice; +static cvar_t *s_alCapture; +#endif + +#ifdef _WIN32 +#define ALDRIVER_DEFAULT "OpenAL32.dll" +#elif defined(MACOS_X) +#define ALDRIVER_DEFAULT "/System/Library/Frameworks/OpenAL.framework/OpenAL" +#else +#define ALDRIVER_DEFAULT "libopenal.so.0" +#endif + +/* +================= +S_AL_StopAllSounds +================= +*/ +static +void S_AL_StopAllSounds( void ) +{ + int i; + S_AL_SrcShutup(); + S_AL_StopBackgroundTrack(); + for (i = 0; i < MAX_RAW_STREAMS; i++) + S_AL_StreamDie(i); +} + +/* +================= +S_AL_Respatialize +================= +*/ +static +void S_AL_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ) +{ + float velocity[3] = {0.0f, 0.0f, 0.0f}; + float orientation[6]; + vec3_t sorigin; + + VectorCopy( origin, sorigin ); + S_AL_SanitiseVector( sorigin ); + + S_AL_SanitiseVector( axis[ 0 ] ); + S_AL_SanitiseVector( axis[ 1 ] ); + S_AL_SanitiseVector( axis[ 2 ] ); + + orientation[0] = axis[0][0]; orientation[1] = axis[0][1]; orientation[2] = axis[0][2]; + orientation[3] = axis[2][0]; orientation[4] = axis[2][1]; orientation[5] = axis[2][2]; + + VectorCopy( sorigin, lastListenerOrigin ); + + // Set OpenAL listener paramaters + qalListenerfv(AL_POSITION, (ALfloat *)sorigin); + qalListenerfv(AL_VELOCITY, velocity); + qalListenerfv(AL_ORIENTATION, orientation); +} + +/* +================= +S_AL_Update +================= +*/ +static +void S_AL_Update( void ) +{ + int i; + + // Update SFX channels + S_AL_SrcUpdate(); + + // Update streams + for (i = 0; i < MAX_RAW_STREAMS; i++) + S_AL_StreamUpdate(i); + S_AL_MusicUpdate(); + + // Doppler + if(s_doppler->modified) + { + s_alDopplerFactor->modified = qtrue; + s_doppler->modified = qfalse; + } + + // Doppler parameters + if(s_alDopplerFactor->modified) + { + if(s_doppler->integer) + qalDopplerFactor(s_alDopplerFactor->value); + else + qalDopplerFactor(0.0f); + s_alDopplerFactor->modified = qfalse; + } + if(s_alDopplerSpeed->modified) + { + qalDopplerVelocity(s_alDopplerSpeed->value); + s_alDopplerSpeed->modified = qfalse; + } + + // Clear the modified flags on the other cvars + s_alGain->modified = qfalse; + s_volume->modified = qfalse; + s_musicVolume->modified = qfalse; + s_alMinDistance->modified = qfalse; + s_alRolloff->modified = qfalse; +} + +/* +================= +S_AL_DisableSounds +================= +*/ +static +void S_AL_DisableSounds( void ) +{ + S_AL_StopAllSounds(); +} + +/* +================= +S_AL_BeginRegistration +================= +*/ +static +void S_AL_BeginRegistration( void ) +{ +} + +/* +================= +S_AL_ClearSoundBuffer +================= +*/ +static +void S_AL_ClearSoundBuffer( void ) +{ +} + +/* +================= +S_AL_SoundList +================= +*/ +static +void S_AL_SoundList( void ) +{ +} + +#ifdef USE_VOIP +static +void S_AL_StartCapture( void ) +{ + if (alCaptureDevice != NULL) + qalcCaptureStart(alCaptureDevice); +} + +static +int S_AL_AvailableCaptureSamples( void ) +{ + int retval = 0; + if (alCaptureDevice != NULL) + { + ALint samples = 0; + qalcGetIntegerv(alCaptureDevice, ALC_CAPTURE_SAMPLES, sizeof (samples), &samples); + retval = (int) samples; + } + return retval; +} + +static +void S_AL_Capture( int samples, byte *data ) +{ + if (alCaptureDevice != NULL) + qalcCaptureSamples(alCaptureDevice, data, samples); +} + +void S_AL_StopCapture( void ) +{ + if (alCaptureDevice != NULL) + qalcCaptureStop(alCaptureDevice); +} + +void S_AL_MasterGain( float gain ) +{ + qalListenerf(AL_GAIN, gain); +} +#endif + + +/* +================= +S_AL_SoundInfo +================= +*/ +static +void S_AL_SoundInfo( void ) +{ + Com_Printf( "OpenAL info:\n" ); + Com_Printf( " Vendor: %s\n", qalGetString( AL_VENDOR ) ); + Com_Printf( " Version: %s\n", qalGetString( AL_VERSION ) ); + Com_Printf( " Renderer: %s\n", qalGetString( AL_RENDERER ) ); + Com_Printf( " AL Extensions: %s\n", qalGetString( AL_EXTENSIONS ) ); + Com_Printf( " ALC Extensions: %s\n", qalcGetString( alDevice, ALC_EXTENSIONS ) ); + if(qalcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT")) + { + Com_Printf(" Device: %s\n", qalcGetString(alDevice, ALC_DEVICE_SPECIFIER)); + Com_Printf("Available Devices:\n%s", s_alAvailableDevices->string); + } +} + +/* +================= +S_AL_Shutdown +================= +*/ +static +void S_AL_Shutdown( void ) +{ + // Shut down everything + int i; + for (i = 0; i < MAX_RAW_STREAMS; i++) + S_AL_StreamDie(i); + S_AL_StopBackgroundTrack( ); + S_AL_SrcShutdown( ); + S_AL_BufferShutdown( ); + + qalcDestroyContext(alContext); + qalcCloseDevice(alDevice); + +#ifdef USE_VOIP + if (alCaptureDevice != NULL) { + qalcCaptureStop(alCaptureDevice); + qalcCaptureCloseDevice(alCaptureDevice); + alCaptureDevice = NULL; + Com_Printf( "OpenAL capture device closed.\n" ); + } +#endif + + for (i = 0; i < MAX_RAW_STREAMS; i++) { + streamSourceHandles[i] = -1; + streamPlaying[i] = qfalse; + streamSources[i] = 0; + } + + QAL_Shutdown(); +} + +#endif + +/* +================= +S_AL_Init +================= +*/ +qboolean S_AL_Init( soundInterface_t *si ) +{ +#ifdef USE_OPENAL + const char* device = NULL; + int i; + + if( !si ) { + return qfalse; + } + + for (i = 0; i < MAX_RAW_STREAMS; i++) { + streamSourceHandles[i] = -1; + streamPlaying[i] = qfalse; + streamSources[i] = 0; + } + + // New console variables + s_alPrecache = Cvar_Get( "s_alPrecache", "1", CVAR_ARCHIVE ); + s_alGain = Cvar_Get( "s_alGain", "1.0", CVAR_ARCHIVE ); + s_alSources = Cvar_Get( "s_alSources", "96", CVAR_ARCHIVE ); + s_alDopplerFactor = Cvar_Get( "s_alDopplerFactor", "1.0", CVAR_ARCHIVE ); + s_alDopplerSpeed = Cvar_Get( "s_alDopplerSpeed", "2200", CVAR_ARCHIVE ); + s_alMinDistance = Cvar_Get( "s_alMinDistance", "120", CVAR_CHEAT ); + s_alMaxDistance = Cvar_Get("s_alMaxDistance", "1024", CVAR_CHEAT); + s_alRolloff = Cvar_Get( "s_alRolloff", "2", CVAR_CHEAT); + s_alGraceDistance = Cvar_Get("s_alGraceDistance", "512", CVAR_CHEAT); + + s_alDriver = Cvar_Get( "s_alDriver", ALDRIVER_DEFAULT, CVAR_ARCHIVE | CVAR_LATCH ); + + s_alDevice = Cvar_Get("s_alDevice", "", CVAR_ARCHIVE | CVAR_LATCH); + + // Load QAL + if( !QAL_Init( s_alDriver->string ) ) + { + Com_Printf( "Failed to load library: \"%s\".\n", s_alDriver->string ); + return qfalse; + } + + device = s_alDevice->string; + if(device && !*device) + device = NULL; + + // Device enumeration support (extension is implemented reasonably only on Windows right now). + if(qalcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT")) + { + char devicenames[1024] = ""; + const char *devicelist; + const char *defaultdevice; + int curlen; + + // get all available devices + the default device name. + devicelist = qalcGetString(NULL, ALC_DEVICE_SPECIFIER); + defaultdevice = qalcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); + +#ifdef _WIN32 + // check whether the default device is generic hardware. If it is, change to + // Generic Software as that one works more reliably with various sound systems. + // If it's not, use OpenAL's default selection as we don't want to ignore + // native hardware acceleration. + if(!device && !strcmp(defaultdevice, "Generic Hardware")) + device = "Generic Software"; +#endif + + // dump a list of available devices to a cvar for the user to see. + while((curlen = strlen(devicelist))) + { + Q_strcat(devicenames, sizeof(devicenames), devicelist); + Q_strcat(devicenames, sizeof(devicenames), "\n"); + + devicelist += curlen + 1; + } + + s_alAvailableDevices = Cvar_Get("s_alAvailableDevices", devicenames, CVAR_ROM | CVAR_NORESTART); + } + + alDevice = qalcOpenDevice(device); + if( !alDevice && device ) + { + Com_Printf( "Failed to open OpenAL device '%s', trying default.\n", device ); + alDevice = qalcOpenDevice(NULL); + } + + if( !alDevice ) + { + QAL_Shutdown( ); + Com_Printf( "Failed to open OpenAL device.\n" ); + return qfalse; + } + + // Create OpenAL context + alContext = qalcCreateContext( alDevice, NULL ); + if( !alContext ) + { + QAL_Shutdown( ); + qalcCloseDevice( alDevice ); + Com_Printf( "Failed to create OpenAL context.\n" ); + return qfalse; + } + qalcMakeContextCurrent( alContext ); + + // Initialize sources, buffers, music + S_AL_BufferInit( ); + S_AL_SrcInit( ); + + // Set up OpenAL parameters (doppler, etc) + qalDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); + qalDopplerFactor( s_alDopplerFactor->value ); + qalDopplerVelocity( s_alDopplerSpeed->value ); + +#ifdef USE_VOIP + // !!! FIXME: some of these alcCaptureOpenDevice() values should be cvars. + // !!! FIXME: add support for capture device enumeration. + // !!! FIXME: add some better error reporting. + s_alCapture = Cvar_Get( "s_alCapture", "1", CVAR_ARCHIVE | CVAR_LATCH ); + if (!s_alCapture->integer) + { + Com_Printf("OpenAL capture support disabled by user ('+set s_alCapture 1' to enable)\n"); + } +#if USE_MUMBLE + else if (cl_useMumble->integer) + { + Com_Printf("OpenAL capture support disabled for Mumble support\n"); + } +#endif + else + { +#ifdef MACOS_X + // !!! FIXME: Apple has a 1.1-compliant OpenAL, which includes + // !!! FIXME: capture support, but they don't list it in the + // !!! FIXME: extension string. We need to check the version string, + // !!! FIXME: then the extension string, but that's too much trouble, + // !!! FIXME: so we'll just check the function pointer for now. + if (qalcCaptureOpenDevice == NULL) +#else + if (!qalcIsExtensionPresent(NULL, "ALC_EXT_capture")) +#endif + { + Com_Printf("No ALC_EXT_capture support, can't record audio.\n"); + } + else + { + // !!! FIXME: 8000Hz is what Speex narrowband mode needs, but we + // !!! FIXME: should probably open the capture device after + // !!! FIXME: initializing Speex so we can change to wideband + // !!! FIXME: if we like. + Com_Printf("OpenAL default capture device is '%s'\n", + qalcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER)); + alCaptureDevice = qalcCaptureOpenDevice(NULL, 8000, AL_FORMAT_MONO16, 4096); + Com_Printf( "OpenAL capture device %s.\n", + (alCaptureDevice == NULL) ? "failed to open" : "opened"); + } + } +#endif + + si->Shutdown = S_AL_Shutdown; + si->StartSound = S_AL_StartSound; + si->StartLocalSound = S_AL_StartLocalSound; + si->StartBackgroundTrack = S_AL_StartBackgroundTrack; + si->StopBackgroundTrack = S_AL_StopBackgroundTrack; + si->RawSamples = S_AL_RawSamples; + si->StopAllSounds = S_AL_StopAllSounds; + si->ClearLoopingSounds = S_AL_ClearLoopingSounds; + si->AddLoopingSound = S_AL_AddLoopingSound; + si->AddRealLoopingSound = S_AL_AddRealLoopingSound; + si->StopLoopingSound = S_AL_StopLoopingSound; + si->Respatialize = S_AL_Respatialize; + si->UpdateEntityPosition = S_AL_UpdateEntityPosition; + si->Update = S_AL_Update; + si->DisableSounds = S_AL_DisableSounds; + si->BeginRegistration = S_AL_BeginRegistration; + si->RegisterSound = S_AL_RegisterSound; + si->ClearSoundBuffer = S_AL_ClearSoundBuffer; + si->SoundInfo = S_AL_SoundInfo; + si->SoundList = S_AL_SoundList; + +#ifdef USE_VOIP + si->StartCapture = S_AL_StartCapture; + si->AvailableCaptureSamples = S_AL_AvailableCaptureSamples; + si->Capture = S_AL_Capture; + si->StopCapture = S_AL_StopCapture; + si->MasterGain = S_AL_MasterGain; +#endif + + return qtrue; +#else + return qfalse; +#endif +} + diff --git a/reaction/engine/code/client/snd_public.h b/reaction/engine/code/client/snd_public.h new file mode 100644 index 00000000..1587f33f --- /dev/null +++ b/reaction/engine/code/client/snd_public.h @@ -0,0 +1,82 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + + +void S_Init( void ); +void S_Shutdown( void ); + +// if origin is NULL, the sound will be dynamically sourced from the entity +void S_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx ); +void S_StartLocalSound( sfxHandle_t sfx, int channelNum ); + +void S_StartBackgroundTrack( const char *intro, const char *loop ); +void S_StopBackgroundTrack( void ); + +// cinematics and voice-over-network will send raw samples +// 1.0 volume will be direct output of source samples +void S_RawSamples (int stream, int samples, int rate, int width, int channels, + const byte *data, float volume); + +// stop all sounds and the background track +void S_StopAllSounds( void ); + +// all continuous looping sounds must be added before calling S_Update +void S_ClearLoopingSounds( qboolean killall ); +void S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); +void S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); +void S_StopLoopingSound(int entityNum ); + +// recompute the reletive volumes for all running sounds +// reletive to the given entityNum / orientation +void S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ); + +// let the sound system know where an entity currently is +void S_UpdateEntityPosition( int entityNum, const vec3_t origin ); + +void S_Update( void ); + +void S_DisableSounds( void ); + +void S_BeginRegistration( void ); + +// RegisterSound will allways return a valid sample, even if it +// has to create a placeholder. This prevents continuous filesystem +// checks for missing files +sfxHandle_t S_RegisterSound( const char *sample, qboolean compressed ); + +void S_DisplayFreeMemory(void); + +void S_ClearSoundBuffer( void ); + +void SNDDMA_Activate( void ); + +void S_UpdateBackgroundTrack( void ); + + +#ifdef USE_VOIP +void S_StartCapture( void ); +int S_AvailableCaptureSamples( void ); +void S_Capture( int samples, byte *data ); +void S_StopCapture( void ); +void S_MasterGain( float gain ); +#endif + diff --git a/reaction/engine/code/client/snd_wavelet.c b/reaction/engine/code/client/snd_wavelet.c new file mode 100644 index 00000000..41b5723a --- /dev/null +++ b/reaction/engine/code/client/snd_wavelet.c @@ -0,0 +1,253 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "snd_local.h" + +long myftol( float f ); + +#define C0 0.4829629131445341 +#define C1 0.8365163037378079 +#define C2 0.2241438680420134 +#define C3 -0.1294095225512604 + +void daub4(float b[], unsigned long n, int isign) +{ + float wksp[4097]; + float *a=b-1; // numerical recipies so a[1] = b[0] + + unsigned long nh,nh1,i,j; + + if (n < 4) return; + + nh1=(nh=n >> 1)+1; + if (isign >= 0) { + for (i=1,j=1;j<=n-3;j+=2,i++) { + wksp[i] = C0*a[j]+C1*a[j+1]+C2*a[j+2]+C3*a[j+3]; + wksp[i+nh] = C3*a[j]-C2*a[j+1]+C1*a[j+2]-C0*a[j+3]; + } + wksp[i ] = C0*a[n-1]+C1*a[n]+C2*a[1]+C3*a[2]; + wksp[i+nh] = C3*a[n-1]-C2*a[n]+C1*a[1]-C0*a[2]; + } else { + wksp[1] = C2*a[nh]+C1*a[n]+C0*a[1]+C3*a[nh1]; + wksp[2] = C3*a[nh]-C0*a[n]+C1*a[1]-C2*a[nh1]; + for (i=1,j=3;i= 0) { + for (nn=n;nn>=inverseStartLength;nn>>=1) daub4(a,nn,isign); + } else { + for (nn=inverseStartLength;nn<=n;nn<<=1) daub4(a,nn,isign); + } +} + +/* The number of bits required by each value */ +static unsigned char numBits[] = { + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +}; + +byte MuLawEncode(short s) { + unsigned long adjusted; + byte sign, exponent, mantissa; + + sign = (s<0)?0:0x80; + + if (s<0) s=-s; + adjusted = (long)s << (16-sizeof(short)*8); + adjusted += 128L + 4L; + if (adjusted > 32767) adjusted = 32767; + exponent = numBits[(adjusted>>7)&0xff] - 1; + mantissa = (adjusted>>(exponent+3))&0xf; + return ~(sign | (exponent<<4) | mantissa); +} + +short MuLawDecode(byte uLaw) { + signed long adjusted; + byte exponent, mantissa; + + uLaw = ~uLaw; + exponent = (uLaw>>4) & 0x7; + mantissa = (uLaw&0xf) + 16; + adjusted = (mantissa << (exponent +3)) - 128 - 4; + + return (uLaw & 0x80)? adjusted : -adjusted; +} + +short mulawToShort[256]; +static qboolean madeTable = qfalse; + +static int NXStreamCount; + +void NXPutc(NXStream *stream, char out) { + stream[NXStreamCount++] = out; +} + + +void encodeWavelet( sfx_t *sfx, short *packets) { + float wksp[4097], temp; + int i, samples, size; + sndBuffer *newchunk, *chunk; + byte *out; + + if (!madeTable) { + for (i=0;i<256;i++) { + mulawToShort[i] = (float)MuLawDecode((byte)i); + } + madeTable = qtrue; + } + chunk = NULL; + + samples = sfx->soundLength; + while(samples>0) { + size = samples; + if (size>(SND_CHUNK_SIZE*2)) { + size = (SND_CHUNK_SIZE*2); + } + + if (size<4) { + size = 4; + } + + newchunk = SND_malloc(); + if (sfx->soundData == NULL) { + sfx->soundData = newchunk; + } else { + chunk->next = newchunk; + } + chunk = newchunk; + for(i=0; isndChunk; + + for(i=0;i 32767) temp = 32767; else if (temp<-32768) temp = -32768; + out[i] = MuLawEncode((short)temp); + } + + chunk->size = size; + samples -= size; + } +} + +void decodeWavelet(sndBuffer *chunk, short *to) { + float wksp[4097]; + int i; + byte *out; + + int size = chunk->size; + + out = (byte *)chunk->sndChunk; + for(i=0;isoundLength; + grade = 0; + + while(samples>0) { + size = samples; + if (size>(SND_CHUNK_SIZE*2)) { + size = (SND_CHUNK_SIZE*2); + } + + newchunk = SND_malloc(); + if (sfx->soundData == NULL) { + sfx->soundData = newchunk; + } else { + chunk->next = newchunk; + } + chunk = newchunk; + out = (byte *)chunk->sndChunk; + for(i=0; i32767) { + poop = 32767; + } else if (poop<-32768) { + poop = -32768; + } + out[i] = MuLawEncode((short)poop); + grade = poop - mulawToShort[out[i]]; + packets++; + } + chunk->size = size; + samples -= size; + } +} + +void decodeMuLaw(sndBuffer *chunk, short *to) { + int i; + byte *out; + + int size = chunk->size; + + out = (byte *)chunk->sndChunk; + for(i=0;icur_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_PLASMA: + case MOD_PLASMA_SPLASH: return "Plasmagun"; + case MOD_RAILGUN: return "Railgun"; + case MOD_LIGHTNING: return "Lightning Gun"; + case MOD_BFG: + case MOD_BFG_SPLASH: return "BFG10K"; +#ifdef MISSIONPACK + case MOD_NAIL: return "Nailgun"; + case MOD_CHAINGUN: return "Chaingun"; + case MOD_PROXIMITY_MINE: return "Proximity Launcher"; + case MOD_KAMIKAZE: return "Kamikaze"; + case MOD_JUICED: return "Prox mine"; +#endif + case MOD_GRAPPLE: return "Grapple"; + default: return "[unknown weapon]"; + } +} + +/* +================== +BotRandomWeaponName +================== +*/ +char *BotRandomWeaponName(void) { + int rnd; + +#ifdef MISSIONPACK + rnd = random() * 11.9; +#else + rnd = random() * 8.9; +#endif + 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"; +#ifdef MISSIONPACK + case 8: return "Nailgun"; + case 9: return "Chaingun"; + case 10: return "Proximity Launcher"; +#endif + default: return "BFG10K"; + } +} + +/* +================== +BotVisibleEnemies +================== +*/ +int BotVisibleEnemies(bot_state_t *bs) { + float vis; + int i; + aas_entityinfo_t entinfo; + + for (i = 0; i < MAX_CLIENTS; i++) { + + if (i == bs->client) continue; + // + BotEntityInfo(i, &entinfo); + // + if (!entinfo.valid) continue; + //if the enemy isn't dead and the enemy isn't the bot self + if (EntityIsDead(&entinfo) || entinfo.number == bs->entitynum) continue; + //if the enemy is invisible and not shooting + if (EntityIsInvisible(&entinfo) && !EntityIsShooting(&entinfo)) { + continue; + } + //if on the same team + if (BotSameTeam(bs, i)) continue; + //check if the enemy is visible + vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); + if (vis > 0) return qtrue; + } + return qfalse; +} + +/* +================== +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; + //never start chatting with a powerup + if (bs->inventory[INVENTORY_QUAD] || + bs->inventory[INVENTORY_HASTE] || + bs->inventory[INVENTORY_INVISIBILITY] || + bs->inventory[INVENTORY_REGEN] || + bs->inventory[INVENTORY_FLIGHT]) return qfalse; + //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 > FloatTime() - TIME_BETWEENCHATTING) return qfalse; + //don't chat in teamplay + if (TeamPlayIsOn()) return qfalse; + // don't chat in tournament mode + if (gametype == GT_TOURNAMENT) 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 = FloatTime(); + 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 > FloatTime() - TIME_BETWEENCHATTING) return qfalse; + //don't chat in teamplay + if (TeamPlayIsOn()) return qfalse; + // don't chat in tournament mode + if (gametype == GT_TOURNAMENT) 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 = FloatTime(); + 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 > FloatTime() - TIME_BETWEENCHATTING) return qfalse; + //don't chat in teamplay + if (TeamPlayIsOn()) { + trap_EA_Command(bs->client, "vtaunt"); + return qfalse; + } + // don't chat in tournament mode + if (gametype == GT_TOURNAMENT) 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 = FloatTime(); + 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 > FloatTime() - TIME_BETWEENCHATTING) return qfalse; + // teamplay + if (TeamPlayIsOn()) + { + if (BotIsFirstInRankings(bs)) { + trap_EA_Command(bs->client, "vtaunt"); + } + return qtrue; + } + // don't chat in tournament mode + if (gametype == GT_TOURNAMENT) 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 = FloatTime(); + 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 > FloatTime() - TIME_BETWEENCHATTING) return qfalse; + rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_DEATH, 0, 1); + // don't chat in tournament mode + if (gametype == GT_TOURNAMENT) return qfalse; + //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 + { + //teamplay + if (TeamPlayIsOn()) { + trap_EA_Command(bs->client, "vtaunt"); + return qtrue; + } + // + 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); +#ifdef MISSIONPACK + else if (bs->botdeathtype == MOD_KAMIKAZE && trap_BotNumInitialChats(bs->cs, "death_kamikaze")) + BotAI_BotInitialChat(bs, "death_kamikaze", name, NULL); +#endif + 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 = FloatTime(); + 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 > FloatTime() - TIME_BETWEENCHATTING) return qfalse; + rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_KILL, 0, 1); + // don't chat in tournament mode + if (gametype == GT_TOURNAMENT) return qfalse; + //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; + // + if (BotVisibleEnemies(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()) { + trap_EA_Command(bs->client, "vtaunt"); + return qfalse; // don't wait + } + // + 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); + } +#ifdef MISSIONPACK + else if (bs->botdeathtype == MOD_KAMIKAZE && trap_BotNumInitialChats(bs->cs, "kill_kamikaze")) + BotAI_BotInitialChat(bs, "kill_kamikaze", name, NULL); +#endif + //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 = FloatTime(); + 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 > FloatTime() - TIME_BETWEENCHATTING) 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; + // don't chat in tournament mode + if (gametype == GT_TOURNAMENT) return qfalse; + //if fast chat is off + if (!bot_fastchat.integer) { + if (random() > rnd) return qfalse; + } + if (!BotValidChatPosition(bs)) return qfalse; + // + if (BotVisibleEnemies(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 = FloatTime(); + 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 > FloatTime() - TIME_BETWEENCHATTING) 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; + // don't chat in tournament mode + if (gametype == GT_TOURNAMENT) 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 = FloatTime(); + 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 > FloatTime() - TIME_BETWEENCHATTING) 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; + // don't chat in tournament mode + if (gametype == GT_TOURNAMENT) return qfalse; + //if fast chat is off + if (!bot_fastchat.integer) { + if (random() > rnd * 0.5) return qfalse; + } + if (!BotValidChatPosition(bs)) return qfalse; + // + if (BotVisibleEnemies(bs)) 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 = FloatTime(); + 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 > FloatTime() - TIME_BETWEENCHATTING) 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; + // don't chat in tournament mode + if (gametype == GT_TOURNAMENT) return qfalse; + //if fast chat is off + if (!bot_fastchat.integer) { + if (random() > rnd * 0.5) return qfalse; + } + if (!BotValidChatPosition(bs)) return qfalse; + // + if (BotVisibleEnemies(bs)) 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 = FloatTime(); + 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 > FloatTime() - TIME_BETWEENCHATTING) return qfalse; + // don't chat in tournament mode + if (gametype == GT_TOURNAMENT) 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 (BotVisibleEnemies(bs)) return qfalse; + // + if (bs->lastkilledplayer == bs->client) { + strcpy(name, BotRandomOpponentName(bs)); + } + else { + EasyClientName(bs->lastkilledplayer, name, sizeof(name)); + } + if (TeamPlayIsOn()) { + trap_EA_Command(bs->client, "vtaunt"); + return qfalse; // don't wait + } + // + 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 = FloatTime(); + 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, CHAT_ALL); + } +} diff --git a/reaction/engine/code/game/ai_chat.h b/reaction/engine/code/game/ai_chat.h new file mode 100644 index 00000000..e458554a --- /dev/null +++ b/reaction/engine/code/game/ai_chat.h @@ -0,0 +1,61 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: ai_chat.h + * + * desc: Quake3 bot AI + * + * $Archive: /source/code/botai/ai_chat.c $ + * + *****************************************************************************/ + +// +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/reaction/engine/code/game/ai_cmd.c b/reaction/engine/code/game/ai_cmd.c new file mode 100644 index 00000000..db954858 --- /dev/null +++ b/reaction/engine/code/game/ai_cmd.c @@ -0,0 +1,1992 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: ai_cmd.c + * + * desc: Quake3 bot AI + * + * $Archive: /MissionPack/code/game/ai_cmd.c $ + * + *****************************************************************************/ + +#include "g_local.h" +#include "../botlib/botlib.h" +#include "../botlib/be_aas.h" +#include "../botlib/be_ea.h" +#include "../botlib/be_ai_char.h" +#include "../botlib/be_ai_chat.h" +#include "../botlib/be_ai_gen.h" +#include "../botlib/be_ai_goal.h" +#include "../botlib/be_ai_move.h" +#include "../botlib/be_ai_weap.h" +// +#include "ai_main.h" +#include "ai_dmq3.h" +#include "ai_chat.h" +#include "ai_cmd.h" +#include "ai_dmnet.h" +#include "ai_team.h" +// +#include "chars.h" //characteristics +#include "inv.h" //indexes into the inventory +#include "syn.h" //synonyms +#include "match.h" //string matching types and vars + +// for the voice chats +#include "../../ui/menudef.h" + +int notleader[MAX_CLIENTS]; + +#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 - FloatTime(); + 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; + } +#ifdef MISSIONPACK + case LTG_ATTACKENEMYBASE: + { + BotAI_Print(PRT_MESSAGE, "%s: I'm gonna attack the enemy base for %1.0f secs\n", netname, t); + break; + } + case LTG_HARVEST: + { + BotAI_Print(PRT_MESSAGE, "%s: I'm gonna harvest for %1.0f secs\n", netname, t); + break; + } +#endif + 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 > FloatTime()) { + t = bs->ctfroam_time - FloatTime(); + 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) { + //do NOT defend dropped items + if (goal->flags & GFL_DROPPED) + continue; + 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.0f; + } + else if (timematch.type == MSG_FORAWHILE) { + t = 10 * 60; // 10 minutes + } + else if (timematch.type == MSG_FORALONGTIME) { + t = 30 * 60; // 30 minutes + } + 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 FloatTime() + 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, 0, CHAT_TEAM); + BotFreeWaypoints(newpatrolpoints); + bs->patrolpoints = NULL; + return qfalse; + } + //create a new waypoint + newwp = BotCreateWayPoint(keyarea, goal.origin, goal.areanum); + if (!newwp) + break; + //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 = ClientOnSameTeamFromName(bs, netname); + if (client < 0) 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 { + bot_match_t tellmatch; + + tellmatch.type = 0; + //if this message wasn't directed solely to this bot + if (!trap_BotFindMatch(match->string, &tellmatch, MTCONTEXT_REPLYCHAT) || + tellmatch.type != MSG_CHATTELL) { + //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]; + char 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); + client = ClientFromName(netname); + trap_BotEnterChat(bs->cs, client, CHAT_TELL); + 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); + client = ClientFromName(netname); + trap_BotEnterChat(bs->cs, client, CHAT_TEAM); + return; + } + //the team mate + bs->teammate = client; + // + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + // + client = ClientFromName(netname); + //the team mate who ordered + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //last time the team mate was assumed visible + bs->teammatevisible_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 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 = FloatTime() + TEAM_HELP_TIME; + } + else { + bs->ltgtype = LTG_TEAMACCOMPANY; + if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; + bs->formation_dist = 3.5 * 32; //3.5 meter + bs->arrive_time = 0; + // + BotSetTeamStatus(bs); + // remember last ordered task + BotRememberLastOrderedTask(bs); + } +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotMatch_DefendKeyArea +================== +*/ +void BotMatch_DefendKeyArea(bot_state_t *bs, bot_match_t *match) { + char itemname[MAX_MESSAGE_SIZE]; + char netname[MAX_MESSAGE_SIZE]; + int client; + + 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; + } + // + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + // + client = ClientFromName(netname); + //the team mate who ordered + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 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 = FloatTime() + TEAM_DEFENDKEYAREA_TIME; + //away from defending + bs->defendaway_time = 0; + // + BotSetTeamStatus(bs); + // remember last ordered task + BotRememberLastOrderedTask(bs); +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotMatch_GetItem +================== +*/ +void BotMatch_GetItem(bot_state_t *bs, bot_match_t *match) { + char itemname[MAX_MESSAGE_SIZE]; + char netname[MAX_MESSAGE_SIZE]; + int client; + + 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; + } + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + client = ClientOnSameTeamFromName(bs, netname); + // + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_GETITEM; + //set the team goal time + bs->teamgoal_time = FloatTime() + TEAM_GETITEM_TIME; + // + BotSetTeamStatus(bs); +#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 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); + client = ClientFromName(netname); + trap_BotEnterChat(bs->cs, client, CHAT_TELL); + return; + } + } + else if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { + //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); + //client = ClientFromName(netname); + //trap_BotEnterChat(bs->cs, client, CHAT_TELL); + return; + } + // + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 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 = FloatTime() + TEAM_CAMP_TIME; + //not arrived yet + bs->arrive_time = 0; + // + BotSetTeamStatus(bs); + // remember last ordered task + BotRememberLastOrderedTask(bs); +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotMatch_Patrol +================== +*/ +void BotMatch_Patrol(bot_state_t *bs, bot_match_t *match) { + char netname[MAX_MESSAGE_SIZE]; + int client; + + if (!TeamPlayIsOn()) return; + //if not addressed to this bot + if (!BotAddressedToBot(bs, match)) return; + //get the patrol waypoints + if (!BotGetPatrolWaypoints(bs, match)) return; + // + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + // + client = FindClientByName(netname); + // + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 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 = FloatTime() + TEAM_PATROL_TIME; + // + BotSetTeamStatus(bs); + // remember last ordered task + BotRememberLastOrderedTask(bs); +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotMatch_GetFlag +================== +*/ +void BotMatch_GetFlag(bot_state_t *bs, bot_match_t *match) { + char netname[MAX_MESSAGE_SIZE]; + int client; + + if (gametype == GT_CTF) { + if (!ctf_redflag.areanum || !ctf_blueflag.areanum) + return; + } +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF) { + if (!ctf_neutralflag.areanum || !ctf_redflag.areanum || !ctf_blueflag.areanum) + return; + } +#endif + else { + return; + } + //if not addressed to this bot + if (!BotAddressedToBot(bs, match)) return; + // + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + // + client = FindClientByName(netname); + // + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_GETFLAG; + //set the team goal time + bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME; + // get an alternate route in ctf + if (gametype == GT_CTF) { + //get an alternative route goal towards the enemy base + BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); + } + // + BotSetTeamStatus(bs); + // remember last ordered task + BotRememberLastOrderedTask(bs); +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotMatch_AttackEnemyBase +================== +*/ +void BotMatch_AttackEnemyBase(bot_state_t *bs, bot_match_t *match) { + char netname[MAX_MESSAGE_SIZE]; + int client; + + if (gametype == GT_CTF) { + BotMatch_GetFlag(bs, match); + } +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF || gametype == GT_OBELISK || gametype == GT_HARVESTER) { + if (!redobelisk.areanum || !blueobelisk.areanum) + return; + } +#endif + else { + return; + } + //if not addressed to this bot + if (!BotAddressedToBot(bs, match)) return; + // + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + // + client = FindClientByName(netname); + // + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_ATTACKENEMYBASE; + //set the team goal time + bs->teamgoal_time = FloatTime() + TEAM_ATTACKENEMYBASE_TIME; + bs->attackaway_time = 0; + // + BotSetTeamStatus(bs); + // remember last ordered task + BotRememberLastOrderedTask(bs); +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +#ifdef MISSIONPACK +/* +================== +BotMatch_Harvest +================== +*/ +void BotMatch_Harvest(bot_state_t *bs, bot_match_t *match) { + char netname[MAX_MESSAGE_SIZE]; + int client; + + if (gametype == GT_HARVESTER) { + if (!neutralobelisk.areanum || !redobelisk.areanum || !blueobelisk.areanum) + return; + } + else { + return; + } + //if not addressed to this bot + if (!BotAddressedToBot(bs, match)) return; + // + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + // + client = FindClientByName(netname); + // + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_HARVEST; + //set the team goal time + bs->teamgoal_time = FloatTime() + TEAM_HARVEST_TIME; + bs->harvestaway_time = 0; + // + BotSetTeamStatus(bs); + // remember last ordered task + BotRememberLastOrderedTask(bs); +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} +#endif + +/* +================== +BotMatch_RushBase +================== +*/ +void BotMatch_RushBase(bot_state_t *bs, bot_match_t *match) { + char netname[MAX_MESSAGE_SIZE]; + int client; + + if (gametype == GT_CTF) { + if (!ctf_redflag.areanum || !ctf_blueflag.areanum) + return; + } +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF || gametype == GT_HARVESTER) { + if (!redobelisk.areanum || !blueobelisk.areanum) + return; + } +#endif + else { + return; + } + //if not addressed to this bot + if (!BotAddressedToBot(bs, match)) return; + // + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + // + client = FindClientByName(netname); + // + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_RUSHBASE; + //set the team goal time + bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; + bs->rushbaseaway_time = 0; + // + BotSetTeamStatus(bs); +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotMatch_TaskPreference +================== +*/ +void BotMatch_TaskPreference(bot_state_t *bs, bot_match_t *match) { + char netname[MAX_NETNAME]; + char teammatename[MAX_MESSAGE_SIZE]; + int teammate, preference; + + ClientName(bs->client, netname, sizeof(netname)); + if (Q_stricmp(netname, bs->teamleader) != 0) return; + + trap_BotMatchVariable(match, NETNAME, teammatename, sizeof(teammatename)); + teammate = ClientFromName(teammatename); + if (teammate < 0) return; + + preference = BotGetTeamMateTaskPreference(bs, teammate); + switch(match->subtype) + { + case ST_DEFENDER: + { + preference &= ~TEAMTP_ATTACKER; + preference |= TEAMTP_DEFENDER; + break; + } + case ST_ATTACKER: + { + preference &= ~TEAMTP_DEFENDER; + preference |= TEAMTP_ATTACKER; + break; + } + case ST_ROAMER: + { + preference &= ~(TEAMTP_ATTACKER|TEAMTP_DEFENDER); + break; + } + } + BotSetTeamMateTaskPreference(bs, teammate, preference); + // + EasyClientName(teammate, teammatename, sizeof(teammatename)); + BotAI_BotInitialChat(bs, "keepinmind", teammatename, NULL); + trap_BotEnterChat(bs->cs, teammate, CHAT_TELL); + BotVoiceChatOnly(bs, teammate, VOICECHAT_YES); + trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); +} + +/* +================== +BotMatch_ReturnFlag +================== +*/ +void BotMatch_ReturnFlag(bot_state_t *bs, bot_match_t *match) { + char netname[MAX_MESSAGE_SIZE]; + int client; + + //if not in CTF mode + if ( + gametype != GT_CTF +#ifdef MISSIONPACK + && gametype != GT_1FCTF +#endif + ) + return; + //if not addressed to this bot + if (!BotAddressedToBot(bs, match)) + return; + // + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + // + client = FindClientByName(netname); + // + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_RETURNFLAG; + //set the team goal time + bs->teamgoal_time = FloatTime() + CTF_RETURNFLAG_TIME; + bs->rushbaseaway_time = 0; + // + BotSetTeamStatus(bs); +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotMatch_JoinSubteam +================== +*/ +void BotMatch_JoinSubteam(bot_state_t *bs, bot_match_t *match) { + char teammate[MAX_MESSAGE_SIZE]; + char netname[MAX_MESSAGE_SIZE]; + int client; + + if (!TeamPlayIsOn()) return; + //if not addressed to this bot + if (!BotAddressedToBot(bs, match)) return; + //get the sub team name + trap_BotMatchVariable(match, TEAMNAME, teammate, sizeof(teammate)); + //set the sub team name + strncpy(bs->subteam, teammate, 32); + bs->subteam[31] = '\0'; + // + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + BotAI_BotInitialChat(bs, "joinedteam", teammate, NULL); + client = ClientFromName(netname); + trap_BotEnterChat(bs->cs, client, CHAT_TELL); +} + +/* +================== +BotMatch_LeaveSubteam +================== +*/ +void BotMatch_LeaveSubteam(bot_state_t *bs, bot_match_t *match) { + char netname[MAX_MESSAGE_SIZE]; + int client; + + 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); + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + client = ClientFromName(netname); + trap_BotEnterChat(bs->cs, client, CHAT_TELL); + } //end if + 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, client; + char buf[MAX_MESSAGE_SIZE]; + char netname[MAX_MESSAGE_SIZE]; + vec3_t position; + bot_waypoint_t *cp; + + if (!TeamPlayIsOn()) return; + // + trap_BotMatchVariable(match, POSITION, buf, MAX_MESSAGE_SIZE); + VectorClear(position); + // + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + client = ClientFromName(netname); + //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, client, CHAT_TELL); + } + 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, client, CHAT_TELL); + } +} + +/* +================== +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) { + char netname[MAX_MESSAGE_SIZE]; + int client; + + if (!TeamPlayIsOn()) return; + //if not addressed to this bot + if (!BotAddressedToBot(bs, match)) return; + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + client = ClientFromName(netname); + // + bs->decisionmaker = client; + // + bs->ltgtype = 0; + bs->lead_time = 0; + bs->lastgoal_ltgtype = 0; + // + BotAI_BotInitialChat(bs, "dismissed", NULL); + trap_BotEnterChat(bs->cs, client, CHAT_TELL); +} + +/* +================== +BotMatch_Suicide +================== +*/ +void BotMatch_Suicide(bot_state_t *bs, bot_match_t *match) { + char netname[MAX_MESSAGE_SIZE]; + int client; + + if (!TeamPlayIsOn()) return; + //if not addressed to this bot + if (!BotAddressedToBot(bs, match)) return; + // + trap_EA_Command(bs->client, "kill"); + // + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + client = ClientFromName(netname); + // + BotVoiceChat(bs, client, VOICECHAT_TAUNT); + trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); +} + +/* +================== +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)-1] = '\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'; + notleader[client] = qtrue; + } + } +} + +/* +================== +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]; + int client; + + //if not addressed to this bot + if (!BotAddressedToBot(bs, match)) return; + // + switch(bs->ltgtype) { + case LTG_TEAMHELP: + { + EasyClientName(bs->teammate, netname, sizeof(netname)); + BotAI_BotInitialChat(bs, "helping", netname, NULL); + break; + } + case LTG_TEAMACCOMPANY: + { + EasyClientName(bs->teammate, netname, sizeof(netname)); + 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; + } +#ifdef MISSIONPACK + case LTG_ATTACKENEMYBASE: + { + BotAI_BotInitialChat(bs, "attackingenemybase", NULL); + break; + } + case LTG_HARVEST: + { + BotAI_BotInitialChat(bs, "harvesting", NULL); + break; + } +#endif + default: + { + BotAI_BotInitialChat(bs, "roaming", NULL); + break; + } + } + //chat what the bot is doing + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + client = ClientFromName(netname); + trap_BotEnterChat(bs->cs, client, CHAT_TELL); +} + +/* +================== +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, redtt, bluett, client; + bot_goal_t goal; + char netname[MAX_MESSAGE_SIZE]; + 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", +#ifdef MISSIONPACK + "Nailgun", + "Prox Launcher", + "Chaingun", + "Scout", + "Guard", + "Doubler", + "Ammo Regen", + "Neutral Flag", + "Red Obelisk", + "Blue Obelisk", + "Neutral Obelisk", +#endif + 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 +#ifdef MISSIONPACK + || gametype == GT_1FCTF +#endif + ) { + redtt = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, ctf_redflag.areanum, TFL_DEFAULT); + bluett = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, ctf_blueflag.areanum, TFL_DEFAULT); + if (redtt < (redtt + bluett) * 0.4) { + BotAI_BotInitialChat(bs, "teamlocation", nearbyitems[bestitem], "red", NULL); + } + else if (bluett < (redtt + bluett) * 0.4) { + BotAI_BotInitialChat(bs, "teamlocation", nearbyitems[bestitem], "blue", NULL); + } + else { + BotAI_BotInitialChat(bs, "location", nearbyitems[bestitem], NULL); + } + } +#ifdef MISSIONPACK + else if (gametype == GT_OBELISK || gametype == GT_HARVESTER) { + redtt = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, redobelisk.areanum, TFL_DEFAULT); + bluett = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, blueobelisk.areanum, TFL_DEFAULT); + if (redtt < (redtt + bluett) * 0.4) { + BotAI_BotInitialChat(bs, "teamlocation", nearbyitems[bestitem], "red", NULL); + } + else if (bluett < (redtt + bluett) * 0.4) { + BotAI_BotInitialChat(bs, "teamlocation", nearbyitems[bestitem], "blue", NULL); + } + else { + BotAI_BotInitialChat(bs, "location", nearbyitems[bestitem], NULL); + } + } +#endif + else { + BotAI_BotInitialChat(bs, "location", nearbyitems[bestitem], NULL); + } + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + client = ClientFromName(netname); + trap_BotEnterChat(bs->cs, client, CHAT_TELL); + } +} + +/* +================== +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 = FloatTime() + TEAM_LEAD_TIME; + bs->leadvisible_time = 0; + bs->leadmessage_time = -(FloatTime() + 2 * random()); +} + +/* +================== +BotMatch_Kill +================== +*/ +void BotMatch_Kill(bot_state_t *bs, bot_match_t *match) { + char enemy[MAX_MESSAGE_SIZE]; + char netname[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_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + client = ClientFromName(netname); + trap_BotEnterChat(bs->cs, client, CHAT_TELL); + return; + } + bs->teamgoal.entitynum = client; + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_KILL; + //set the team goal time + bs->teamgoal_time = FloatTime() + TEAM_KILL_SOMEONE; + // + BotSetTeamStatus(bs); +#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]; + + if (gametype == GT_CTF) { + trap_BotMatchVariable(match, FLAG, flag, sizeof(flag)); + if (match->subtype & ST_GOTFLAG) { + if (!Q_stricmp(flag, "red")) { + bs->redflagstatus = 1; + if (BotTeam(bs) == TEAM_BLUE) { + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + bs->flagcarrier = ClientFromName(netname); + } + } + else { + bs->blueflagstatus = 1; + if (BotTeam(bs) == TEAM_RED) { + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + bs->flagcarrier = ClientFromName(netname); + } + } + bs->flagstatuschanged = 1; + bs->lastflagcapture_time = FloatTime(); + } + 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; + } + } +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF) { + if (match->subtype & ST_1FCTFGOTFLAG) { + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + bs->flagcarrier = ClientFromName(netname); + } + } +#endif +} + +void BotMatch_EnterGame(bot_state_t *bs, bot_match_t *match) { + int client; + char netname[MAX_NETNAME]; + + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + client = FindClientByName(netname); + if (client >= 0) { + notleader[client] = qfalse; + } + //NOTE: eliza chats will catch this + //Com_sprintf(buf, sizeof(buf), "heya %s", netname); + //EA_Say(bs->client, buf); +} + +void BotMatch_NewLeader(bot_state_t *bs, bot_match_t *match) { + int client; + char netname[MAX_NETNAME]; + + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + client = FindClientByName(netname); + if (!BotSameTeam(bs, client)) + return; + Q_strncpyz(bs->teamleader, netname, sizeof(bs->teamleader)); +} + +/* +================== +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_MISC + |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; + } + //CTF & 1FCTF + case MSG_GETFLAG: //ctf get the enemy flag + { + BotMatch_GetFlag(bs, &match); + break; + } +#ifdef MISSIONPACK + //CTF & 1FCTF & Obelisk & Harvester + case MSG_ATTACKENEMYBASE: + { + BotMatch_AttackEnemyBase(bs, &match); + break; + } + //Harvester + case MSG_HARVEST: + { + BotMatch_Harvest(bs, &match); + break; + } +#endif + //CTF & 1FCTF & Harvester + case MSG_RUSHBASE: //ctf rush to the base + { + BotMatch_RushBase(bs, &match); + break; + } + //CTF & 1FCTF + case MSG_RETURNFLAG: + { + BotMatch_ReturnFlag(bs, &match); + break; + } + //CTF & 1FCTF & Obelisk & Harvester + case MSG_TASKPREFERENCE: + { + BotMatch_TaskPreference(bs, &match); + break; + } + //CTF & 1FCTF + case MSG_CTF: + { + BotMatch_CTF(bs, &match); + break; + } + 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 + { + BotMatch_EnterGame(bs, &match); + break; + } + case MSG_NEWLEADER: + { + BotMatch_NewLeader(bs, &match); + break; + } + case MSG_WAIT: + { + break; + } + case MSG_SUICIDE: + { + BotMatch_Suicide(bs, &match); + break; + } + default: + { + BotAI_Print(PRT_MESSAGE, "unknown match type\n"); + break; + } + } + return qtrue; +} diff --git a/reaction/engine/code/game/ai_cmd.h b/reaction/engine/code/game/ai_cmd.h new file mode 100644 index 00000000..dd10bc19 --- /dev/null +++ b/reaction/engine/code/game/ai_cmd.h @@ -0,0 +1,37 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: ai_cmd.h + * + * desc: Quake3 bot AI + * + * $Archive: /source/code/botai/ai_chat.c $ + * + *****************************************************************************/ + +extern int notleader[MAX_CLIENTS]; + +int BotMatchMessage(bot_state_t *bs, char *message); +void BotPrintTeamGoal(bot_state_t *bs); + diff --git a/reaction/engine/code/game/ai_dmnet.c b/reaction/engine/code/game/ai_dmnet.c new file mode 100644 index 00000000..5050bd19 --- /dev/null +++ b/reaction/engine/code/game/ai_dmnet.c @@ -0,0 +1,2610 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: ai_dmnet.c + * + * desc: Quake3 bot AI + * + * $Archive: /MissionPack/code/game/ai_dmnet.c $ + * + *****************************************************************************/ + +#include "g_local.h" +#include "../botlib/botlib.h" +#include "../botlib/be_aas.h" +#include "../botlib/be_ea.h" +#include "../botlib/be_ai_char.h" +#include "../botlib/be_ai_chat.h" +#include "../botlib/be_ai_gen.h" +#include "../botlib/be_ai_goal.h" +#include "../botlib/be_ai_move.h" +#include "../botlib/be_ai_weap.h" +// +#include "ai_main.h" +#include "ai_dmq3.h" +#include "ai_chat.h" +#include "ai_cmd.h" +#include "ai_dmnet.h" +#include "ai_team.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 + +// for the voice chats +#include "../../ui/menudef.h" + +//goal flag, see ../botlib/be_ai_goal.h for the other GFL_* +#define GFL_AIR 128 + +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, FloatTime(), MAX_NODESWITCHES); + for (i = 0; i < numnodeswitches; i++) { + BotAI_Print(PRT_MESSAGE, "%s", nodeswitch[i]); + } + BotAI_Print(PRT_FATAL, ""); +} + +/* +================== +BotRecordNodeSwitch +================== +*/ +void BotRecordNodeSwitch(bot_state_t *bs, char *node, char *str, char *s) { + char netname[MAX_NETNAME]; + + ClientName(bs->client, netname, sizeof(netname)); + Com_sprintf(nodeswitch[numnodeswitches], 144, "%s at %2.1f entered %s: %s from %s\n", netname, FloatTime(), node, str, s); +#ifdef DEBUG + if (0) { + BotAI_Print(PRT_MESSAGE, "%s", 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 < FloatTime() - 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; + + //check if the bot should go for air + if (BotGoForAir(bs, tfl, ltg, range)) return qtrue; + //if the bot is carrying the enemy flag + if (BotCTFCarryingFlag(bs)) { + //if the bot is just a few secs away from the base + if (trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, + bs->teamgoal.areanum, TFL_DEFAULT) < 300) { + //make the range really small + range = 50; + } + } + // + 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", FloatTime(), 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)) { + if (!(goal->flags & GFL_DROPPED)) { + trap_BotSetAvoidGoalTime(bs->gs, goal->number, -1); + } + return qtrue; + } + //if the goal isn't there + if (trap_BotItemGoalInVisButNotVisible(bs->entitynum, bs->eye, bs->viewangles, goal)) { + /* + float avoidtime; + int t; + + avoidtime = trap_BotAvoidGoalTime(bs->gs, goal->number); + if (avoidtime > 0) { + t = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, goal->areanum, bs->tfl); + if ((float) t * 0.009 < avoidtime) + return qtrue; + } + */ + 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 > FloatTime() - 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 < FloatTime()) { + //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", FloatTime(), 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", FloatTime(), buf); + */ + bs->ltg_time = FloatTime() + 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, dir2; + char netname[MAX_NETNAME]; + char buf[MAX_MESSAGE_SIZE]; + int areanum; + float croucher; + aas_entityinfo_t entinfo, botinfo; + bot_waypoint_t *wp; + + if (bs->ltgtype == LTG_TEAMHELP && !retreat) { + //check for bot typing status message + if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { + BotAI_BotInitialChat(bs, "help_start", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); + trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); + BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES); + trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); + bs->teammessage_time = 0; + } + //if trying to help the team mate for more than a minute + if (bs->teamgoal_time < FloatTime()) + bs->ltgtype = 0; + //if the team mate IS visible for quite some time + if (bs->teammatevisible_time < FloatTime() - 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 (VectorLengthSquared(dir) < Square(100)) { + trap_BotResetAvoidReach(bs->ms); + return qfalse; + } + } + else { + //last time the bot was NOT visible + bs->teammatevisible_time = FloatTime(); + } + //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 < FloatTime()) { + BotAI_BotInitialChat(bs, "accompany_start", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); + trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); + BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES); + trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); + bs->teammessage_time = 0; + } + //if accompanying the companion for 3 minutes + if (bs->teamgoal_time < FloatTime()) { + BotAI_BotInitialChat(bs, "accompany_stop", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); + trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); + 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 = FloatTime(); + VectorSubtract(entinfo.origin, bs->origin, dir); + if (VectorLengthSquared(dir) < Square(bs->formation_dist)) { + // + // if the client being followed bumps into this bot then + // the bot should back up + BotEntityInfo(bs->entitynum, &botinfo); + // if the followed client is not standing ontop of the bot + if (botinfo.origin[2] + botinfo.maxs[2] > entinfo.origin[2] + entinfo.mins[2]) { + // if the bounding boxes touch each other + if (botinfo.origin[0] + botinfo.maxs[0] > entinfo.origin[0] + entinfo.mins[0] - 4&& + botinfo.origin[0] + botinfo.mins[0] < entinfo.origin[0] + entinfo.maxs[0] + 4) { + if (botinfo.origin[1] + botinfo.maxs[1] > entinfo.origin[1] + entinfo.mins[1] - 4 && + botinfo.origin[1] + botinfo.mins[1] < entinfo.origin[1] + entinfo.maxs[1] + 4) { + if (botinfo.origin[2] + botinfo.maxs[2] > entinfo.origin[2] + entinfo.mins[2] - 4 && + botinfo.origin[2] + botinfo.mins[2] < entinfo.origin[2] + entinfo.maxs[2] + 4) { + // if the followed client looks in the direction of this bot + AngleVectors(entinfo.angles, dir, NULL, NULL); + dir[2] = 0; + VectorNormalize(dir); + //VectorSubtract(entinfo.origin, entinfo.lastvisorigin, dir); + VectorSubtract(bs->origin, entinfo.origin, dir2); + VectorNormalize(dir2); + if (DotProduct(dir, dir2) > 0.7) { + // back up + BotSetupForMovement(bs); + trap_BotMoveInDirection(bs->ms, dir2, 400, MOVE_WALK); + } + } + } + } + } + //check if the bot wants to crouch + //don't crouch if crouched less than 5 seconds ago + if (bs->attackcrouch_time < FloatTime() - 5) { + croucher = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CROUCHER, 0, 1); + if (random() < bs->thinktime * croucher) { + bs->attackcrouch_time = FloatTime() + 5 + croucher * 15; + } + } + //don't crouch when swimming + if (trap_AAS_Swimming(bs->origin)) bs->attackcrouch_time = FloatTime() - 1; + //if not arrived yet or arived some time ago + if (bs->arrive_time < FloatTime() - 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->teammate, CHAT_TELL); + bs->arrive_time = FloatTime(); + } + //if the bot wants to crouch + else if (bs->attackcrouch_time > FloatTime()) { + trap_EA_Crouch(bs->client); + } + //else do some model taunts + else if (random() < bs->thinktime * 0.05) { + //do a gesture :) + trap_EA_Gesture(bs->client); + } + } + //if just arrived look at the companion + if (bs->arrive_time > FloatTime() - 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 = FloatTime() + 8; + AIEnter_Seek_NBG(bs, "BotLongTermGoal: go for air"); + 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 + 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 < FloatTime() - 60) { + BotAI_BotInitialChat(bs, "accompany_cannotfind", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); + trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); + bs->ltgtype = 0; + // just to make sure the bot won't spam this message + bs->teammatevisible_time = FloatTime(); + } + 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 < FloatTime()) { + //check for bot typing status message + if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { + trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); + BotAI_BotInitialChat(bs, "defend_start", buf, NULL); + trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); + BotVoiceChatOnly(bs, -1, VOICECHAT_ONDEFENSE); + bs->teammessage_time = 0; + } + //set the bot goal + memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t)); + //stop after 2 minutes + if (bs->teamgoal_time < FloatTime()) { + trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); + BotAI_BotInitialChat(bs, "defend_stop", buf, NULL); + trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); + bs->ltgtype = 0; + } + //if very close... go away for some time + VectorSubtract(goal->origin, bs->origin, dir); + if (VectorLengthSquared(dir) < Square(70)) { + trap_BotResetAvoidReach(bs->ms); + bs->defendaway_time = FloatTime() + 3 + 3 * random(); + if (BotHasPersistantPowerupAndWeapon(bs)) { + bs->defendaway_range = 100; + } + else { + bs->defendaway_range = 350; + } + } + 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 < FloatTime()) { + EasyClientName(bs->teamgoal.entitynum, buf, sizeof(buf)); + BotAI_BotInitialChat(bs, "kill_start", buf, NULL); + trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); + 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->decisionmaker, CHAT_TELL); + bs->lastkilledplayer = -1; + bs->ltgtype = 0; + } + // + if (bs->teamgoal_time < FloatTime()) { + 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 < FloatTime()) { + trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); + BotAI_BotInitialChat(bs, "getitem_start", buf, NULL); + trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); + BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES); + trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); + bs->teammessage_time = 0; + } + //set the bot goal + memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t)); + //stop after some time + if (bs->teamgoal_time < FloatTime()) { + 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->decisionmaker, CHAT_TELL); + 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->decisionmaker, CHAT_TELL); + 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 < FloatTime()) { + if (bs->ltgtype == LTG_CAMPORDER) { + BotAI_BotInitialChat(bs, "camp_start", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); + trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); + BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES); + trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); + } + bs->teammessage_time = 0; + } + //set the bot goal + memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t)); + // + if (bs->teamgoal_time < FloatTime()) { + if (bs->ltgtype == LTG_CAMPORDER) { + BotAI_BotInitialChat(bs, "camp_stop", NULL); + trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); + } + bs->ltgtype = 0; + } + //if really near the camp spot + VectorSubtract(goal->origin, bs->origin, dir); + if (VectorLengthSquared(dir) < Square(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->decisionmaker, CHAT_TELL); + BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_INPOSITION); + } + bs->arrive_time = FloatTime(); + } + //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 < FloatTime() - 5) { + croucher = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CROUCHER, 0, 1); + if (random() < bs->thinktime * croucher) { + bs->attackcrouch_time = FloatTime() + 5 + croucher * 15; + } + } + //if the bot wants to crouch + if (bs->attackcrouch_time > FloatTime()) { + trap_EA_Crouch(bs->client); + } + //don't crouch when swimming + if (trap_AAS_Swimming(bs->origin)) bs->attackcrouch_time = FloatTime() - 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->decisionmaker, CHAT_TELL); + // + if (bs->lastgoal_ltgtype == LTG_CAMPORDER) { + bs->lastgoal_ltgtype = 0; + } + } + 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 < FloatTime()) { + 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->decisionmaker, CHAT_TELL); + BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES); + trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); + 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 < FloatTime()) { + BotAI_BotInitialChat(bs, "patrol_stop", NULL); + trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); + 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 (gametype == GT_CTF) { + //if going for enemy flag + if (bs->ltgtype == LTG_GETFLAG) { + //check for bot typing status message + if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { + BotAI_BotInitialChat(bs, "captureflag_start", NULL); + trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); + BotVoiceChatOnly(bs, -1, VOICECHAT_ONGETFLAG); + bs->teammessage_time = 0; + } + // + switch(BotTeam(bs)) { + case TEAM_RED: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break; + case TEAM_BLUE: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break; + default: bs->ltgtype = 0; return qfalse; + } + //if touching the flag + if (trap_BotTouchingGoal(bs->origin, goal)) { + // make sure the bot knows the flag isn't there anymore + switch(BotTeam(bs)) { + case TEAM_RED: bs->blueflagstatus = 1; break; + case TEAM_BLUE: bs->redflagstatus = 1; break; + } + bs->ltgtype = 0; + } + //stop after 3 minutes + if (bs->teamgoal_time < FloatTime()) { + bs->ltgtype = 0; + } + BotAlternateRoute(bs, goal); + return qtrue; + } + //if rushing to the base + if (bs->ltgtype == LTG_RUSHBASE && bs->rushbaseaway_time < FloatTime()) { + switch(BotTeam(bs)) { + case TEAM_RED: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break; + case TEAM_BLUE: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break; + default: bs->ltgtype = 0; return qfalse; + } + //if not carrying the flag anymore + if (!BotCTFCarryingFlag(bs)) bs->ltgtype = 0; + //quit rushing after 2 minutes + if (bs->teamgoal_time < FloatTime()) 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 = FloatTime() + 5 + 10 * random(); + //FIXME: add chat to tell the others to get back the flag + } + else { + bs->ltgtype = 0; + } + } + BotAlternateRoute(bs, goal); + return qtrue; + } + //returning flag + if (bs->ltgtype == LTG_RETURNFLAG) { + //check for bot typing status message + if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { + BotAI_BotInitialChat(bs, "returnflag_start", NULL); + trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); + BotVoiceChatOnly(bs, -1, VOICECHAT_ONRETURNFLAG); + bs->teammessage_time = 0; + } + // + switch(BotTeam(bs)) { + case TEAM_RED: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break; + case TEAM_BLUE: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); 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 < FloatTime()) { + bs->ltgtype = 0; + } + BotAlternateRoute(bs, goal); + return qtrue; + } + } +#endif //CTF +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF) { + if (bs->ltgtype == LTG_GETFLAG) { + //check for bot typing status message + if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { + BotAI_BotInitialChat(bs, "captureflag_start", NULL); + trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); + BotVoiceChatOnly(bs, -1, VOICECHAT_ONGETFLAG); + bs->teammessage_time = 0; + } + memcpy(goal, &ctf_neutralflag, sizeof(bot_goal_t)); + //if touching the flag + if (trap_BotTouchingGoal(bs->origin, goal)) { + bs->ltgtype = 0; + } + //stop after 3 minutes + if (bs->teamgoal_time < FloatTime()) { + bs->ltgtype = 0; + } + return qtrue; + } + //if rushing to the base + if (bs->ltgtype == LTG_RUSHBASE) { + switch(BotTeam(bs)) { + case TEAM_RED: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break; + case TEAM_BLUE: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break; + default: bs->ltgtype = 0; return qfalse; + } + //if not carrying the flag anymore + if (!Bot1FCTFCarryingFlag(bs)) { + bs->ltgtype = 0; + } + //quit rushing after 2 minutes + if (bs->teamgoal_time < FloatTime()) { + bs->ltgtype = 0; + } + //if touching the base flag the bot should loose the enemy flag + if (trap_BotTouchingGoal(bs->origin, goal)) { + bs->ltgtype = 0; + } + BotAlternateRoute(bs, goal); + return qtrue; + } + //attack the enemy base + if (bs->ltgtype == LTG_ATTACKENEMYBASE && + bs->attackaway_time < FloatTime()) { + //check for bot typing status message + if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { + BotAI_BotInitialChat(bs, "attackenemybase_start", NULL); + trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); + BotVoiceChatOnly(bs, -1, VOICECHAT_ONOFFENSE); + bs->teammessage_time = 0; + } + switch(BotTeam(bs)) { + case TEAM_RED: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break; + case TEAM_BLUE: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break; + default: bs->ltgtype = 0; return qfalse; + } + //quit rushing after 2 minutes + if (bs->teamgoal_time < FloatTime()) { + bs->ltgtype = 0; + } + //if touching the base flag the bot should loose the enemy flag + if (trap_BotTouchingGoal(bs->origin, goal)) { + bs->attackaway_time = FloatTime() + 2 + 5 * random(); + } + return qtrue; + } + //returning flag + if (bs->ltgtype == LTG_RETURNFLAG) { + //check for bot typing status message + if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { + BotAI_BotInitialChat(bs, "returnflag_start", NULL); + trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); + BotVoiceChatOnly(bs, -1, VOICECHAT_ONRETURNFLAG); + bs->teammessage_time = 0; + } + // + if (bs->teamgoal_time < FloatTime()) { + bs->ltgtype = 0; + } + //just roam around + return BotGetItemLongTermGoal(bs, tfl, goal); + } + } + else if (gametype == GT_OBELISK) { + if (bs->ltgtype == LTG_ATTACKENEMYBASE && + bs->attackaway_time < FloatTime()) { + + //check for bot typing status message + if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { + BotAI_BotInitialChat(bs, "attackenemybase_start", NULL); + trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); + BotVoiceChatOnly(bs, -1, VOICECHAT_ONOFFENSE); + bs->teammessage_time = 0; + } + switch(BotTeam(bs)) { + case TEAM_RED: memcpy(goal, &blueobelisk, sizeof(bot_goal_t)); break; + case TEAM_BLUE: memcpy(goal, &redobelisk, sizeof(bot_goal_t)); break; + default: bs->ltgtype = 0; return qfalse; + } + //if the bot no longer wants to attack the obelisk + if (BotFeelingBad(bs) > 50) { + return BotGetItemLongTermGoal(bs, tfl, goal); + } + //if touching the obelisk + if (trap_BotTouchingGoal(bs->origin, goal)) { + bs->attackaway_time = FloatTime() + 3 + 5 * random(); + } + // or very close to the obelisk + VectorSubtract(bs->origin, goal->origin, dir); + if (VectorLengthSquared(dir) < Square(60)) { + bs->attackaway_time = FloatTime() + 3 + 5 * random(); + } + //quit rushing after 2 minutes + if (bs->teamgoal_time < FloatTime()) { + bs->ltgtype = 0; + } + BotAlternateRoute(bs, goal); + //just move towards the obelisk + return qtrue; + } + } + else if (gametype == GT_HARVESTER) { + //if rushing to the base + if (bs->ltgtype == LTG_RUSHBASE) { + switch(BotTeam(bs)) { + case TEAM_RED: memcpy(goal, &blueobelisk, sizeof(bot_goal_t)); break; + case TEAM_BLUE: memcpy(goal, &redobelisk, sizeof(bot_goal_t)); break; + default: BotGoHarvest(bs); return qfalse; + } + //if not carrying any cubes + if (!BotHarvesterCarryingCubes(bs)) { + BotGoHarvest(bs); + return qfalse; + } + //quit rushing after 2 minutes + if (bs->teamgoal_time < FloatTime()) { + BotGoHarvest(bs); + return qfalse; + } + //if touching the base flag the bot should loose the enemy flag + if (trap_BotTouchingGoal(bs->origin, goal)) { + BotGoHarvest(bs); + return qfalse; + } + BotAlternateRoute(bs, goal); + return qtrue; + } + //attack the enemy base + if (bs->ltgtype == LTG_ATTACKENEMYBASE && + bs->attackaway_time < FloatTime()) { + //check for bot typing status message + if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { + BotAI_BotInitialChat(bs, "attackenemybase_start", NULL); + trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); + BotVoiceChatOnly(bs, -1, VOICECHAT_ONOFFENSE); + bs->teammessage_time = 0; + } + switch(BotTeam(bs)) { + case TEAM_RED: memcpy(goal, &blueobelisk, sizeof(bot_goal_t)); break; + case TEAM_BLUE: memcpy(goal, &redobelisk, sizeof(bot_goal_t)); break; + default: bs->ltgtype = 0; return qfalse; + } + //quit rushing after 2 minutes + if (bs->teamgoal_time < FloatTime()) { + bs->ltgtype = 0; + } + //if touching the base flag the bot should loose the enemy flag + if (trap_BotTouchingGoal(bs->origin, goal)) { + bs->attackaway_time = FloatTime() + 2 + 5 * random(); + } + return qtrue; + } + //harvest cubes + if (bs->ltgtype == LTG_HARVEST && + bs->harvestaway_time < FloatTime()) { + //check for bot typing status message + if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { + BotAI_BotInitialChat(bs, "harvest_start", NULL); + trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); + BotVoiceChatOnly(bs, -1, VOICECHAT_ONOFFENSE); + bs->teammessage_time = 0; + } + memcpy(goal, &neutralobelisk, sizeof(bot_goal_t)); + // + if (bs->teamgoal_time < FloatTime()) { + bs->ltgtype = 0; + } + // + if (trap_BotTouchingGoal(bs->origin, goal)) { + bs->harvestaway_time = FloatTime() + 4 + 3 * random(); + } + return qtrue; + } + } +#endif + //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 squaredist; + 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 < FloatTime()) { + BotAI_BotInitialChat(bs, "lead_stop", EasyClientName(bs->lead_teammate, teammate, sizeof(teammate)), NULL); + trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); + bs->lead_time = 0; + return BotGetLongTermGoal(bs, tfl, retreat, goal); + } + // + if (bs->leadmessage_time < 0 && -bs->leadmessage_time < FloatTime()) { + BotAI_BotInitialChat(bs, "followme", EasyClientName(bs->lead_teammate, teammate, sizeof(teammate)), NULL); + trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); + bs->leadmessage_time = FloatTime(); + } + //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 = FloatTime(); + } + //if the team mate is not visible for 1 seconds + if (bs->leadvisible_time < FloatTime() - 1) { + bs->leadbackup_time = FloatTime() + 2; + } + //distance towards the team mate + VectorSubtract(bs->origin, bs->lead_teamgoal.origin, dir); + squaredist = VectorLengthSquared(dir); + //if backing up towards the team mate + if (bs->leadbackup_time > FloatTime()) { + if (bs->leadmessage_time < FloatTime() - 20) { + BotAI_BotInitialChat(bs, "followme", EasyClientName(bs->lead_teammate, teammate, sizeof(teammate)), NULL); + trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); + bs->leadmessage_time = FloatTime(); + } + //if very close to the team mate + if (squaredist < Square(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 (squaredist > Square(500)) { + if (bs->leadmessage_time < FloatTime() - 20) { + BotAI_BotInitialChat(bs, "followme", EasyClientName(bs->lead_teammate, teammate, sizeof(teammate)), NULL); + trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); + bs->leadmessage_time = FloatTime(); + } + //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, char *s) { + BotRecordNodeSwitch(bs, "intermission", "", s); + //reset the bot state + BotResetState(bs); + //check for end level chat + if (BotChat_EndLevel(bs)) { + trap_BotEnterChat(bs->cs, 0, 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 = FloatTime() + BotChatTime(bs); + } + else { + bs->stand_time = FloatTime() + 2; + } + AIEnter_Stand(bs, "intermission: chat"); + } + return qtrue; +} + +/* +================== +AIEnter_Observer +================== +*/ +void AIEnter_Observer(bot_state_t *bs, char *s) { + BotRecordNodeSwitch(bs, "observer", "", s); + //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, "observer: left observer"); + } + return qtrue; +} + +/* +================== +AIEnter_Stand +================== +*/ +void AIEnter_Stand(bot_state_t *bs, char *s) { + BotRecordNodeSwitch(bs, "stand", "", s); + bs->standfindenemy_time = FloatTime() + 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 = FloatTime() + BotChatTime(bs) + 0.1; + bs->stand_time = FloatTime() + BotChatTime(bs) + 0.1; + } + } + if (bs->standfindenemy_time < FloatTime()) { + if (BotFindEnemy(bs, -1)) { + AIEnter_Battle_Fight(bs, "stand: found enemy"); + return qfalse; + } + bs->standfindenemy_time = FloatTime() + 1; + } + // put up chat icon + trap_EA_Talk(bs->client); + // when done standing + if (bs->stand_time < FloatTime()) { + trap_BotEnterChat(bs->cs, 0, bs->chatto); + AIEnter_Seek_LTG(bs, "stand: time out"); + return qfalse; + } + // + return qtrue; +} + +/* +================== +AIEnter_Respawn +================== +*/ +void AIEnter_Respawn(bot_state_t *bs, char *s) { + BotRecordNodeSwitch(bs, "respawn", "", s); + //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 = FloatTime() + BotChatTime(bs); + bs->respawnchat_time = FloatTime(); + } + else { + bs->respawn_time = FloatTime() + 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 waiting for the actual respawn + if (bs->respawn_wait) { + if (!BotIsDead(bs)) { + AIEnter_Seek_LTG(bs, "respawn: respawned"); + } + else { + trap_EA_Respawn(bs->client); + } + } + else if (bs->respawn_time < FloatTime()) { + // wait until respawned + bs->respawn_wait = qtrue; + // elementary action respawn + trap_EA_Respawn(bs->client); + // + if (bs->respawnchat_time) { + trap_BotEnterChat(bs->cs, 0, bs->chatto); + bs->enemy = -1; + } + } + if (bs->respawnchat_time && bs->respawnchat_time < FloatTime() - 0.5) { + trap_EA_Talk(bs->client); + } + // + return qtrue; +} + +/* +================== +BotSelectActivateWeapon +================== +*/ +int BotSelectActivateWeapon(bot_state_t *bs) { + // + if (bs->inventory[INVENTORY_MACHINEGUN] > 0 && bs->inventory[INVENTORY_BULLETS] > 0) + return WEAPONINDEX_MACHINEGUN; + else if (bs->inventory[INVENTORY_SHOTGUN] > 0 && bs->inventory[INVENTORY_SHELLS] > 0) + return WEAPONINDEX_SHOTGUN; + else if (bs->inventory[INVENTORY_PLASMAGUN] > 0 && bs->inventory[INVENTORY_CELLS] > 0) + return WEAPONINDEX_PLASMAGUN; + else if (bs->inventory[INVENTORY_LIGHTNING] > 0 && bs->inventory[INVENTORY_LIGHTNINGAMMO] > 0) + return WEAPONINDEX_LIGHTNING; +#ifdef MISSIONPACK + else if (bs->inventory[INVENTORY_CHAINGUN] > 0 && bs->inventory[INVENTORY_BELT] > 0) + return WEAPONINDEX_CHAINGUN; + else if (bs->inventory[INVENTORY_NAILGUN] > 0 && bs->inventory[INVENTORY_NAILS] > 0) + return WEAPONINDEX_NAILGUN; +#endif + else if (bs->inventory[INVENTORY_RAILGUN] > 0 && bs->inventory[INVENTORY_SLUGS] > 0) + return WEAPONINDEX_RAILGUN; + else if (bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 && bs->inventory[INVENTORY_ROCKETS] > 0) + return WEAPONINDEX_ROCKET_LAUNCHER; + else if (bs->inventory[INVENTORY_BFG10K] > 0 && bs->inventory[INVENTORY_BFGAMMO] > 0) + return WEAPONINDEX_BFG; + else { + return -1; + } +} + +/* +================== +BotClearPath + + try to deactivate obstacles like proximity mines on the bot's path +================== +*/ +void BotClearPath(bot_state_t *bs, bot_moveresult_t *moveresult) { + int i, bestmine; + float dist, bestdist; + vec3_t target, dir; + bsp_trace_t bsptrace; + entityState_t state; + + // if there is a dead body wearing kamikze nearby + if (bs->kamikazebody) { + // if the bot's view angles and weapon are not used for movement + if ( !(moveresult->flags & (MOVERESULT_MOVEMENTVIEW | MOVERESULT_MOVEMENTWEAPON)) ) { + // + BotAI_GetEntityState(bs->kamikazebody, &state); + VectorCopy(state.pos.trBase, target); + target[2] += 8; + VectorSubtract(target, bs->eye, dir); + vectoangles(dir, moveresult->ideal_viewangles); + // + moveresult->weapon = BotSelectActivateWeapon(bs); + if (moveresult->weapon == -1) { + // FIXME: run away! + moveresult->weapon = 0; + } + if (moveresult->weapon) { + // + moveresult->flags |= MOVERESULT_MOVEMENTWEAPON | MOVERESULT_MOVEMENTVIEW; + // if holding the right weapon + if (bs->cur_ps.weapon == moveresult->weapon) { + // if the bot is pretty close with it's aim + if (InFieldOfVision(bs->viewangles, 20, moveresult->ideal_viewangles)) { + // + BotAI_Trace(&bsptrace, bs->eye, NULL, NULL, target, bs->entitynum, MASK_SHOT); + // if the mine is visible from the current position + if (bsptrace.fraction >= 1.0 || bsptrace.ent == state.number) { + // shoot at the mine + trap_EA_Attack(bs->client); + } + } + } + } + } + } + if (moveresult->flags & MOVERESULT_BLOCKEDBYAVOIDSPOT) { + bs->blockedbyavoidspot_time = FloatTime() + 5; + } + // if blocked by an avoid spot and the view angles and weapon are used for movement + if (bs->blockedbyavoidspot_time > FloatTime() && + !(moveresult->flags & (MOVERESULT_MOVEMENTVIEW | MOVERESULT_MOVEMENTWEAPON)) ) { + bestdist = 300; + bestmine = -1; + for (i = 0; i < bs->numproxmines; i++) { + BotAI_GetEntityState(bs->proxmines[i], &state); + VectorSubtract(state.pos.trBase, bs->origin, dir); + dist = VectorLength(dir); + if (dist < bestdist) { + bestdist = dist; + bestmine = i; + } + } + if (bestmine != -1) { + // + // state->generic1 == TEAM_RED || state->generic1 == TEAM_BLUE + // + // deactivate prox mines in the bot's path by shooting + // rockets or plasma cells etc. at them + BotAI_GetEntityState(bs->proxmines[bestmine], &state); + VectorCopy(state.pos.trBase, target); + target[2] += 2; + VectorSubtract(target, bs->eye, dir); + vectoangles(dir, moveresult->ideal_viewangles); + // if the bot has a weapon that does splash damage + if (bs->inventory[INVENTORY_PLASMAGUN] > 0 && bs->inventory[INVENTORY_CELLS] > 0) + moveresult->weapon = WEAPONINDEX_PLASMAGUN; + else if (bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 && bs->inventory[INVENTORY_ROCKETS] > 0) + moveresult->weapon = WEAPONINDEX_ROCKET_LAUNCHER; + else if (bs->inventory[INVENTORY_BFG10K] > 0 && bs->inventory[INVENTORY_BFGAMMO] > 0) + moveresult->weapon = WEAPONINDEX_BFG; + else { + moveresult->weapon = 0; + } + if (moveresult->weapon) { + // + moveresult->flags |= MOVERESULT_MOVEMENTWEAPON | MOVERESULT_MOVEMENTVIEW; + // if holding the right weapon + if (bs->cur_ps.weapon == moveresult->weapon) { + // if the bot is pretty close with it's aim + if (InFieldOfVision(bs->viewangles, 20, moveresult->ideal_viewangles)) { + // + BotAI_Trace(&bsptrace, bs->eye, NULL, NULL, target, bs->entitynum, MASK_SHOT); + // if the mine is visible from the current position + if (bsptrace.fraction >= 1.0 || bsptrace.ent == state.number) { + // shoot at the mine + trap_EA_Attack(bs->client); + } + } + } + } + } + } +} + +/* +================== +AIEnter_Seek_ActivateEntity +================== +*/ +void AIEnter_Seek_ActivateEntity(bot_state_t *bs, char *s) { + BotRecordNodeSwitch(bs, "activate entity", "", s); + 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, ideal_viewangles; + bot_moveresult_t moveresult; + int targetvisible; + bsp_trace_t bsptrace; + aas_entityinfo_t entinfo; + + if (BotIsObserver(bs)) { + BotClearActivateGoalStack(bs); + AIEnter_Observer(bs, "active entity: observer"); + return qfalse; + } + //if in the intermission + if (BotIntermission(bs)) { + BotClearActivateGoalStack(bs); + AIEnter_Intermission(bs, "activate entity: intermission"); + return qfalse; + } + //respawn if dead + if (BotIsDead(bs)) { + BotClearActivateGoalStack(bs); + AIEnter_Respawn(bs, "activate entity: bot dead"); + 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 (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; + // map specific code + BotMapScripts(bs); + // no enemy + bs->enemy = -1; + // if the bot has no activate goal + if (!bs->activatestack) { + BotClearActivateGoalStack(bs); + AIEnter_Seek_NBG(bs, "activate entity: no goal"); + return qfalse; + } + // + goal = &bs->activatestack->goal; + // initialize target being visible to false + targetvisible = qfalse; + // if the bot has to shoot at a target to activate something + if (bs->activatestack->shoot) { + // + BotAI_Trace(&bsptrace, bs->eye, NULL, NULL, bs->activatestack->target, bs->entitynum, MASK_SHOT); + // if the shootable entity is visible from the current position + if (bsptrace.fraction >= 1.0 || bsptrace.ent == goal->entitynum) { + targetvisible = qtrue; + // if holding the right weapon + if (bs->cur_ps.weapon == bs->activatestack->weapon) { + VectorSubtract(bs->activatestack->target, bs->eye, dir); + vectoangles(dir, ideal_viewangles); + // if the bot is pretty close with it's aim + if (InFieldOfVision(bs->viewangles, 20, ideal_viewangles)) { + trap_EA_Attack(bs->client); + } + } + } + } + // if the shoot target is visible + if (targetvisible) { + // get the entity info of the entity the bot is shooting at + BotEntityInfo(goal->entitynum, &entinfo); + // if the entity the bot shoots at moved + if (!VectorCompare(bs->activatestack->origin, entinfo.origin)) { +#ifdef DEBUG + BotAI_Print(PRT_MESSAGE, "hit shootable button or trigger\n"); +#endif //DEBUG + bs->activatestack->time = 0; + } + // if the activate goal has been activated or the bot takes too long + if (bs->activatestack->time < FloatTime()) { + BotPopFromActivateGoalStack(bs); + // if there are more activate goals on the stack + if (bs->activatestack) { + bs->activatestack->time = FloatTime() + 10; + return qfalse; + } + AIEnter_Seek_NBG(bs, "activate entity: time out"); + return qfalse; + } + memset(&moveresult, 0, sizeof(bot_moveresult_t)); + } + else { + // if the bot has no goal + if (!goal) { + bs->activatestack->time = 0; + } + // if the bot does not have a shoot goal + else if (!bs->activatestack->shoot) { + //if the bot touches the current goal + if (trap_BotTouchingGoal(bs->origin, goal)) { +#ifdef DEBUG + BotAI_Print(PRT_MESSAGE, "touched button or trigger\n"); +#endif //DEBUG + bs->activatestack->time = 0; + } + } + // if the activate goal has been activated or the bot takes too long + if (bs->activatestack->time < FloatTime()) { + BotPopFromActivateGoalStack(bs); + // if there are more activate goals on the stack + if (bs->activatestack) { + bs->activatestack->time = FloatTime() + 10; + return qfalse; + } + AIEnter_Seek_NBG(bs, "activate entity: activated"); + return qfalse; + } + //predict obstacles + if (BotAIPredictObstacles(bs, goal)) + 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->activatestack->time = 0; + } + //check if the bot is blocked + BotAIBlocked(bs, &moveresult, qtrue); + } + // + BotClearPath(bs, &moveresult); + // if the bot has to shoot to activate + if (bs->activatestack->shoot) { + // if the view angles aren't yet used for the movement + if (!(moveresult.flags & MOVERESULT_MOVEMENTVIEW)) { + VectorSubtract(bs->activatestack->target, bs->eye, dir); + vectoangles(dir, moveresult.ideal_viewangles); + moveresult.flags |= MOVERESULT_MOVEMENTVIEW; + } + // if there's no weapon yet used for the movement + if (!(moveresult.flags & MOVERESULT_MOVEMENTWEAPON)) { + moveresult.flags |= MOVERESULT_MOVEMENTWEAPON; + // + bs->activatestack->weapon = BotSelectActivateWeapon(bs); + if (bs->activatestack->weapon == -1) { + //FIXME: find a decent weapon first + bs->activatestack->weapon = 0; + } + moveresult.weapon = bs->activatestack->weapon; + } + } + // if the ideal view angles are set for movement + 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, "activate entity: found enemy"); + } + else { + trap_BotResetLastAvoidReach(bs->ms); + //empty the goal stack + trap_BotEmptyGoalStack(bs->gs); + //go fight + AIEnter_Battle_Fight(bs, "activate entity: found enemy"); + } + BotClearActivateGoalStack(bs); + } + return qtrue; +} + +/* +================== +AIEnter_Seek_NBG +================== +*/ +void AIEnter_Seek_NBG(bot_state_t *bs, char *s) { + bot_goal_t goal; + char buf[144]; + + if (trap_BotGetTopGoal(bs->gs, &goal)) { + trap_BotGoalName(goal.number, buf, 144); + BotRecordNodeSwitch(bs, "seek NBG", buf, s); + } + else { + BotRecordNodeSwitch(bs, "seek NBG", "no goal", s); + } + 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, "seek nbg: observer"); + return qfalse; + } + //if in the intermission + if (BotIntermission(bs)) { + AIEnter_Intermission(bs, "seek nbg: intermision"); + return qfalse; + } + //respawn if dead + if (BotIsDead(bs)) { + AIEnter_Respawn(bs, "seek nbg: bot dead"); + 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 (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; + // + if (BotCanAndWantsToRocketJump(bs)) { + bs->tfl |= TFL_ROCKETJUMP; + } + //map specific code + BotMapScripts(bs); + //no enemy + bs->enemy = -1; + //if the bot has no goal + if (!trap_BotGetTopGoal(bs->gs, &goal)) bs->nbg_time = 0; + //if the bot touches the current goal + else if (BotReachedGoal(bs, &goal)) { + BotChooseWeapon(bs); + bs->nbg_time = 0; + } + // + if (bs->nbg_time < FloatTime()) { + //pop the current goal from the stack + trap_BotPopGoal(bs->gs); + //check for new nearby items right away + //NOTE: we canNOT reset the check_time to zero because it would create an endless loop of node switches + bs->check_time = FloatTime() + 0.05; + //go back to seek ltg + AIEnter_Seek_LTG(bs, "seek nbg: time out"); + return qfalse; + } + //predict obstacles + if (BotAIPredictObstacles(bs, &goal)) + 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); + // + BotClearPath(bs, &moveresult); + //if the viewangles are used for the movement + 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_BotGetSecondGoal(bs->gs, &goal)) trap_BotGetTopGoal(bs->gs, &goal); + if (trap_BotMovementViewTarget(bs->ms, &goal, bs->tfl, 300, target)) { + VectorSubtract(target, bs->origin, dir); + vectoangles(dir, bs->ideal_viewangles); + } + //FIXME: look at cluster portals? + 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, "seek nbg: found enemy"); + } + else { + trap_BotResetLastAvoidReach(bs->ms); + //empty the goal stack + trap_BotEmptyGoalStack(bs->gs); + //go fight + AIEnter_Battle_Fight(bs, "seek nbg: found enemy"); + } + } + return qtrue; +} + +/* +================== +AIEnter_Seek_LTG +================== +*/ +void AIEnter_Seek_LTG(bot_state_t *bs, char *s) { + bot_goal_t goal; + char buf[144]; + + if (trap_BotGetTopGoal(bs->gs, &goal)) { + trap_BotGoalName(goal.number, buf, 144); + BotRecordNodeSwitch(bs, "seek LTG", buf, s); + } + else { + BotRecordNodeSwitch(bs, "seek LTG", "no goal", s); + } + bs->ainode = AINode_Seek_LTG; +} + +/* +================== +AINode_Seek_LTG +================== +*/ +int AINode_Seek_LTG(bot_state_t *bs) +{ + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + int range; + //char buf[128]; + //bot_goal_t tmpgoal; + + if (BotIsObserver(bs)) { + AIEnter_Observer(bs, "seek ltg: observer"); + return qfalse; + } + //if in the intermission + if (BotIntermission(bs)) { + AIEnter_Intermission(bs, "seek ltg: intermission"); + return qfalse; + } + //respawn if dead + if (BotIsDead(bs)) { + AIEnter_Respawn(bs, "seek ltg: bot dead"); + return qfalse; + } + // + if (BotChat_Random(bs)) { + bs->stand_time = FloatTime() + BotChatTime(bs); + AIEnter_Stand(bs, "seek ltg: random chat"); + 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 (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; + // + if (BotCanAndWantsToRocketJump(bs)) { + bs->tfl |= TFL_ROCKETJUMP; + } + //map specific code + BotMapScripts(bs); + //no enemy + bs->enemy = -1; + // + if (bs->killedenemy_time > FloatTime() - 2) { + if (random() < bs->thinktime * 1) { + trap_EA_Gesture(bs->client); + } + } + //if there is an enemy + if (BotFindEnemy(bs, -1)) { + if (BotWantsToRetreat(bs)) { + //keep the current long term goal and retreat + AIEnter_Battle_Retreat(bs, "seek ltg: found enemy"); + return qfalse; + } + else { + trap_BotResetLastAvoidReach(bs->ms); + //empty the goal stack + trap_BotEmptyGoalStack(bs->gs); + //go fight + AIEnter_Battle_Fight(bs, "seek ltg: found enemy"); + return qfalse; + } + } + // + BotTeamGoals(bs, qfalse); + //get the current long term goal + if (!BotLongTermGoal(bs, bs->tfl, qfalse, &goal)) { + return qtrue; + } + //check for nearby goals periodicly + if (bs->check_time < FloatTime()) { + bs->check_time = FloatTime() + 0.5; + //check if the bot wants to camp + BotWantsToCamp(bs); + // + if (bs->ltgtype == LTG_DEFENDKEYAREA) range = 400; + else range = 150; + // +#ifdef CTF + if (gametype == GT_CTF) { + //if carrying a flag the bot shouldn't be distracted too much + if (BotCTFCarryingFlag(bs)) + range = 50; + } +#endif //CTF +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF) { + if (Bot1FCTFCarryingFlag(bs)) + range = 50; + } + else if (gametype == GT_HARVESTER) { + if (BotHarvesterCarryingCubes(bs)) + range = 80; + } +#endif + // + if (BotNearbyGoal(bs, bs->tfl, &goal, range)) { + 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 = FloatTime() + 4 + range * 0.01; + AIEnter_Seek_NBG(bs, "ltg seek: nbg"); + return qfalse; + } + } + //predict obstacles + if (BotAIPredictObstacles(bs, &goal)) + 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); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + bs->ltg_time = 0; + } + // + BotAIBlocked(bs, &moveresult, qtrue); + // + BotClearPath(bs, &moveresult); + //if the viewangles are used for the movement + 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); + } + //FIXME: look at cluster portals? + else if (VectorLengthSquared(moveresult.movedir)) { + vectoangles(moveresult.movedir, bs->ideal_viewangles); + } + 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; + } + bs->ideal_viewangles[2] *= 0.5; + } + //if the weapon is used for the bot movement + if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon; + // + return qtrue; +} + +/* +================== +AIEnter_Battle_Fight +================== +*/ +void AIEnter_Battle_Fight(bot_state_t *bs, char *s) { + BotRecordNodeSwitch(bs, "battle fight", "", s); + trap_BotResetLastAvoidReach(bs->ms); + bs->ainode = AINode_Battle_Fight; +} + +/* +================== +AIEnter_Battle_Fight +================== +*/ +void AIEnter_Battle_SuicidalFight(bot_state_t *bs, char *s) { + BotRecordNodeSwitch(bs, "battle fight", "", s); + trap_BotResetLastAvoidReach(bs->ms); + bs->ainode = AINode_Battle_Fight; + bs->flags |= BFL_FIGHTSUICIDAL; +} + +/* +================== +AINode_Battle_Fight +================== +*/ +int AINode_Battle_Fight(bot_state_t *bs) { + int areanum; + vec3_t target; + aas_entityinfo_t entinfo; + bot_moveresult_t moveresult; + + if (BotIsObserver(bs)) { + AIEnter_Observer(bs, "battle fight: observer"); + return qfalse; + } + + //if in the intermission + if (BotIntermission(bs)) { + AIEnter_Intermission(bs, "battle fight: intermission"); + return qfalse; + } + //respawn if dead + if (BotIsDead(bs)) { + AIEnter_Respawn(bs, "battle fight: bot dead"); + return qfalse; + } + //if there is another better enemy + if (BotFindEnemy(bs, bs->enemy)) { +#ifdef DEBUG + BotAI_Print(PRT_MESSAGE, "found new better enemy\n"); +#endif + } + //if no enemy + if (bs->enemy < 0) { + AIEnter_Seek_LTG(bs, "battle fight: no enemy"); + return qfalse; + } + // + BotEntityInfo(bs->enemy, &entinfo); + //if the enemy is dead + if (bs->enemydeath_time) { + if (bs->enemydeath_time < FloatTime() - 1.0) { + bs->enemydeath_time = 0; + if (bs->enemysuicide) { + BotChat_EnemySuicide(bs); + } + if (bs->lastkilledplayer == bs->enemy && BotChat_Kill(bs)) { + bs->stand_time = FloatTime() + BotChatTime(bs); + AIEnter_Stand(bs, "battle fight: enemy dead"); + } + else { + bs->ltg_time = 0; + AIEnter_Seek_LTG(bs, "battle fight: enemy dead"); + } + return qfalse; + } + } + else { + if (EntityIsDead(&entinfo)) { + bs->enemydeath_time = FloatTime(); + } + } + //if the enemy is invisible and not shooting the bot looses track easily + if (EntityIsInvisible(&entinfo) && !EntityIsShooting(&entinfo)) { + if (random() < 0.2) { + AIEnter_Seek_LTG(bs, "battle fight: invisible"); + return qfalse; + } + } + // + VectorCopy(entinfo.origin, target); + // if not a player enemy + if (bs->enemy >= MAX_CLIENTS) { +#ifdef MISSIONPACK + // if attacking an obelisk + if ( bs->enemy == redobelisk.entitynum || + bs->enemy == blueobelisk.entitynum ) { + target[2] += 16; + } +#endif + } + //update the reachability area and origin if possible + areanum = BotPointAreaNum(target); + if (areanum && trap_AAS_AreaReachability(areanum)) { + VectorCopy(target, bs->lastenemyorigin); + bs->lastenemyareanum = areanum; + } + //update the attack inventory values + BotUpdateBattleInventory(bs, bs->enemy); + //if the bot's health decreased + if (bs->lastframe_health > bs->inventory[INVENTORY_HEALTH]) { + if (BotChat_HitNoDeath(bs)) { + bs->stand_time = FloatTime() + BotChatTime(bs); + AIEnter_Stand(bs, "battle fight: chat health decreased"); + return qfalse; + } + } + //if the bot hit someone + if (bs->cur_ps.persistant[PERS_HITS] > bs->lasthitcount) { + if (BotChat_HitNoKill(bs)) { + bs->stand_time = FloatTime() + BotChatTime(bs); + AIEnter_Stand(bs, "battle fight: chat hit someone"); + return qfalse; + } + } + //if the enemy is not visible + if (!BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy)) { + if (BotWantsToChase(bs)) { + AIEnter_Battle_Chase(bs, "battle fight: enemy out of sight"); + return qfalse; + } + else { + AIEnter_Seek_LTG(bs, "battle fight: enemy out of sight"); + return qfalse; + } + } + //use holdable items + BotBattleUseItems(bs); + // + 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 (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; + // + if (BotCanAndWantsToRocketJump(bs)) { + bs->tfl |= TFL_ROCKETJUMP; + } + //choose the best weapon to fight with + BotChooseWeapon(bs); + //do attack movements + moveresult = BotAttackMove(bs, bs->tfl); + //if the movement failed + if (moveresult.failure) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach(bs->ms); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + bs->ltg_time = 0; + } + // + BotAIBlocked(bs, &moveresult, qfalse); + //aim at the enemy + BotAimAtEnemy(bs); + //attack the enemy if possible + BotCheckAttack(bs); + //if the bot wants to retreat + if (!(bs->flags & BFL_FIGHTSUICIDAL)) { + if (BotWantsToRetreat(bs)) { + AIEnter_Battle_Retreat(bs, "battle fight: wants to retreat"); + return qtrue; + } + } + return qtrue; +} + +/* +================== +AIEnter_Battle_Chase +================== +*/ +void AIEnter_Battle_Chase(bot_state_t *bs, char *s) { + BotRecordNodeSwitch(bs, "battle chase", "", s); + bs->chase_time = FloatTime(); + bs->ainode = AINode_Battle_Chase; +} + +/* +================== +AINode_Battle_Chase +================== +*/ +int AINode_Battle_Chase(bot_state_t *bs) +{ + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + float range; + + if (BotIsObserver(bs)) { + AIEnter_Observer(bs, "battle chase: observer"); + return qfalse; + } + //if in the intermission + if (BotIntermission(bs)) { + AIEnter_Intermission(bs, "battle chase: intermission"); + return qfalse; + } + //respawn if dead + if (BotIsDead(bs)) { + AIEnter_Respawn(bs, "battle chase: bot dead"); + return qfalse; + } + //if no enemy + if (bs->enemy < 0) { + AIEnter_Seek_LTG(bs, "battle chase: no enemy"); + return qfalse; + } + //if the enemy is visible + if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy)) { + AIEnter_Battle_Fight(bs, "battle chase"); + return qfalse; + } + //if there is another enemy + if (BotFindEnemy(bs, -1)) { + AIEnter_Battle_Fight(bs, "battle chase: better enemy"); + return qfalse; + } + //there is no last enemy area + if (!bs->lastenemyareanum) { + AIEnter_Seek_LTG(bs, "battle chase: no enemy area"); + 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 (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; + // + if (BotCanAndWantsToRocketJump(bs)) { + bs->tfl |= TFL_ROCKETJUMP; + } + //map specific code + BotMapScripts(bs); + //create the chase goal + goal.entitynum = bs->enemy; + goal.areanum = bs->lastenemyareanum; + VectorCopy(bs->lastenemyorigin, goal.origin); + VectorSet(goal.mins, -8, -8, -8); + VectorSet(goal.maxs, 8, 8, 8); + //if the last seen enemy spot is reached the enemy could not be found + if (trap_BotTouchingGoal(bs->origin, &goal)) bs->chase_time = 0; + //if there's no chase time left + if (!bs->chase_time || bs->chase_time < FloatTime() - 10) { + AIEnter_Seek_LTG(bs, "battle chase: time out"); + return qfalse; + } + //check for nearby goals periodicly + if (bs->check_time < FloatTime()) { + bs->check_time = FloatTime() + 1; + range = 150; + // + if (BotNearbyGoal(bs, bs->tfl, &goal, range)) { + //the bot gets 5 seconds to pick up the nearby goal item + bs->nbg_time = FloatTime() + 0.1 * range + 1; + trap_BotResetLastAvoidReach(bs->ms); + AIEnter_Battle_NBG(bs, "battle chase: nbg"); + return qfalse; + } + } + // + BotUpdateBattleInventory(bs, bs->enemy); + //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); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + bs->ltg_time = 0; + } + // + BotAIBlocked(bs, &moveresult, qfalse); + // + if (moveresult.flags & (MOVERESULT_MOVEMENTVIEWSET|MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) { + VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles); + } + else if (!(bs->flags & BFL_IDEALVIEWSET)) { + if (bs->chase_time > FloatTime() - 2) { + BotAimAtEnemy(bs); + } + else { + 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 the bot is in the area the enemy was last seen in + if (bs->areanum == bs->lastenemyareanum) bs->chase_time = 0; + //if the bot wants to retreat (the bot could have been damage during the chase) + if (BotWantsToRetreat(bs)) { + AIEnter_Battle_Retreat(bs, "battle chase: wants to retreat"); + return qtrue; + } + return qtrue; +} + +/* +================== +AIEnter_Battle_Retreat +================== +*/ +void AIEnter_Battle_Retreat(bot_state_t *bs, char *s) { + BotRecordNodeSwitch(bs, "battle retreat", "", s); + bs->ainode = AINode_Battle_Retreat; +} + +/* +================== +AINode_Battle_Retreat +================== +*/ +int AINode_Battle_Retreat(bot_state_t *bs) { + bot_goal_t goal; + aas_entityinfo_t entinfo; + bot_moveresult_t moveresult; + vec3_t target, dir; + float attack_skill, range; + int areanum; + + if (BotIsObserver(bs)) { + AIEnter_Observer(bs, "battle retreat: observer"); + return qfalse; + } + //if in the intermission + if (BotIntermission(bs)) { + AIEnter_Intermission(bs, "battle retreat: intermission"); + return qfalse; + } + //respawn if dead + if (BotIsDead(bs)) { + AIEnter_Respawn(bs, "battle retreat: bot dead"); + return qfalse; + } + //if no enemy + if (bs->enemy < 0) { + AIEnter_Seek_LTG(bs, "battle retreat: no enemy"); + return qfalse; + } + // + BotEntityInfo(bs->enemy, &entinfo); + if (EntityIsDead(&entinfo)) { + AIEnter_Seek_LTG(bs, "battle retreat: enemy dead"); + return qfalse; + } + //if there is another better enemy + if (BotFindEnemy(bs, bs->enemy)) { +#ifdef DEBUG + BotAI_Print(PRT_MESSAGE, "found new better enemy\n"); +#endif + } + // + 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 (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; + //map specific code + BotMapScripts(bs); + //update the attack inventory values + BotUpdateBattleInventory(bs, bs->enemy); + //if the bot doesn't want to retreat anymore... probably picked up some nice items + if (BotWantsToChase(bs)) { + //empty the goal stack, when chasing, only the enemy is the goal + trap_BotEmptyGoalStack(bs->gs); + //go chase the enemy + AIEnter_Battle_Chase(bs, "battle retreat: wants to chase"); + return qfalse; + } + //update the last time the enemy was visible + if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy)) { + bs->enemyvisible_time = FloatTime(); + VectorCopy(entinfo.origin, target); + // if not a player enemy + if (bs->enemy >= MAX_CLIENTS) { +#ifdef MISSIONPACK + // if attacking an obelisk + if ( bs->enemy == redobelisk.entitynum || + bs->enemy == blueobelisk.entitynum ) { + target[2] += 16; + } +#endif + } + //update the reachability area and origin if possible + areanum = BotPointAreaNum(target); + if (areanum && trap_AAS_AreaReachability(areanum)) { + VectorCopy(target, bs->lastenemyorigin); + bs->lastenemyareanum = areanum; + } + } + //if the enemy is NOT visible for 4 seconds + if (bs->enemyvisible_time < FloatTime() - 4) { + AIEnter_Seek_LTG(bs, "battle retreat: lost enemy"); + return qfalse; + } + //else if the enemy is NOT visible + else if (bs->enemyvisible_time < FloatTime()) { + //if there is another enemy + if (BotFindEnemy(bs, -1)) { + AIEnter_Battle_Fight(bs, "battle retreat: another enemy"); + return qfalse; + } + } + // + BotTeamGoals(bs, qtrue); + //use holdable items + BotBattleUseItems(bs); + //get the current long term goal while retreating + if (!BotLongTermGoal(bs, bs->tfl, qtrue, &goal)) { + AIEnter_Battle_SuicidalFight(bs, "battle retreat: no way out"); + return qfalse; + } + //check for nearby goals periodicly + if (bs->check_time < FloatTime()) { + bs->check_time = FloatTime() + 1; + range = 150; +#ifdef CTF + if (gametype == GT_CTF) { + //if carrying a flag the bot shouldn't be distracted too much + if (BotCTFCarryingFlag(bs)) + range = 50; + } +#endif //CTF +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF) { + if (Bot1FCTFCarryingFlag(bs)) + range = 50; + } + else if (gametype == GT_HARVESTER) { + if (BotHarvesterCarryingCubes(bs)) + range = 80; + } +#endif + // + if (BotNearbyGoal(bs, bs->tfl, &goal, range)) { + trap_BotResetLastAvoidReach(bs->ms); + //time the bot gets to pick up the nearby goal item + bs->nbg_time = FloatTime() + range / 100 + 1; + AIEnter_Battle_NBG(bs, "battle retreat: nbg"); + 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); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + bs->ltg_time = 0; + } + // + BotAIBlocked(bs, &moveresult, qfalse); + //choose the best weapon to fight with + BotChooseWeapon(bs); + //if the view is fixed for the movement + if (moveresult.flags & (MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) { + VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles); + } + else if (!(moveresult.flags & MOVERESULT_MOVEMENTVIEWSET) + && !(bs->flags & BFL_IDEALVIEWSET) ) { + attack_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ATTACK_SKILL, 0, 1); + //if the bot is skilled anough + if (attack_skill > 0.3) { + BotAimAtEnemy(bs); + } + else { + 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; + //attack the enemy if possible + BotCheckAttack(bs); + // + return qtrue; +} + +/* +================== +AIEnter_Battle_NBG +================== +*/ +void AIEnter_Battle_NBG(bot_state_t *bs, char *s) { + BotRecordNodeSwitch(bs, "battle NBG", "", s); + bs->ainode = AINode_Battle_NBG; +} + +/* +================== +AINode_Battle_NBG +================== +*/ +int AINode_Battle_NBG(bot_state_t *bs) { + int areanum; + bot_goal_t goal; + aas_entityinfo_t entinfo; + bot_moveresult_t moveresult; + float attack_skill; + vec3_t target, dir; + + if (BotIsObserver(bs)) { + AIEnter_Observer(bs, "battle nbg: observer"); + return qfalse; + } + //if in the intermission + if (BotIntermission(bs)) { + AIEnter_Intermission(bs, "battle nbg: intermission"); + return qfalse; + } + //respawn if dead + if (BotIsDead(bs)) { + AIEnter_Respawn(bs, "battle nbg: bot dead"); + return qfalse; + } + //if no enemy + if (bs->enemy < 0) { + AIEnter_Seek_NBG(bs, "battle nbg: no enemy"); + return qfalse; + } + // + BotEntityInfo(bs->enemy, &entinfo); + if (EntityIsDead(&entinfo)) { + AIEnter_Seek_NBG(bs, "battle nbg: enemy dead"); + 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 (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; + // + if (BotCanAndWantsToRocketJump(bs)) { + bs->tfl |= TFL_ROCKETJUMP; + } + //map specific code + BotMapScripts(bs); + //update the last time the enemy was visible + if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy)) { + bs->enemyvisible_time = FloatTime(); + VectorCopy(entinfo.origin, target); + // if not a player enemy + if (bs->enemy >= MAX_CLIENTS) { +#ifdef MISSIONPACK + // if attacking an obelisk + if ( bs->enemy == redobelisk.entitynum || + bs->enemy == blueobelisk.entitynum ) { + target[2] += 16; + } +#endif + } + //update the reachability area and origin if possible + areanum = BotPointAreaNum(target); + if (areanum && trap_AAS_AreaReachability(areanum)) { + VectorCopy(target, bs->lastenemyorigin); + bs->lastenemyareanum = areanum; + } + } + //if the bot has no goal or touches the current goal + if (!trap_BotGetTopGoal(bs->gs, &goal)) { + bs->nbg_time = 0; + } + else if (BotReachedGoal(bs, &goal)) { + bs->nbg_time = 0; + } + // + if (bs->nbg_time < FloatTime()) { + //pop the current goal from the stack + trap_BotPopGoal(bs->gs); + //if the bot still has a goal + if (trap_BotGetTopGoal(bs->gs, &goal)) + AIEnter_Battle_Retreat(bs, "battle nbg: time out"); + else + AIEnter_Battle_Fight(bs, "battle nbg: time out"); + // + 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); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + bs->nbg_time = 0; + } + // + BotAIBlocked(bs, &moveresult, qfalse); + //update the attack inventory values + BotUpdateBattleInventory(bs, bs->enemy); + //choose the best weapon to fight with + BotChooseWeapon(bs); + //if the view is fixed for the movement + if (moveresult.flags & (MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) { + VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles); + } + else if (!(moveresult.flags & MOVERESULT_MOVEMENTVIEWSET) + && !(bs->flags & BFL_IDEALVIEWSET)) { + attack_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ATTACK_SKILL, 0, 1); + //if the bot is skilled anough and the enemy is visible + if (attack_skill > 0.3) { + //&& BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy) + BotAimAtEnemy(bs); + } + else { + 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; + //attack the enemy if possible + BotCheckAttack(bs); + // + return qtrue; +} + diff --git a/reaction/engine/code/game/ai_dmnet.h b/reaction/engine/code/game/ai_dmnet.h new file mode 100644 index 00000000..05ed2ed1 --- /dev/null +++ b/reaction/engine/code/game/ai_dmnet.h @@ -0,0 +1,61 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: ai_dmnet.h + * + * desc: Quake3 bot AI + * + * $Archive: /source/code/botai/ai_chat.c $ + * + *****************************************************************************/ + +#define MAX_NODESWITCHES 50 + +void AIEnter_Intermission(bot_state_t *bs, char *s); +void AIEnter_Observer(bot_state_t *bs, char *s); +void AIEnter_Respawn(bot_state_t *bs, char *s); +void AIEnter_Stand(bot_state_t *bs, char *s); +void AIEnter_Seek_ActivateEntity(bot_state_t *bs, char *s); +void AIEnter_Seek_NBG(bot_state_t *bs, char *s); +void AIEnter_Seek_LTG(bot_state_t *bs, char *s); +void AIEnter_Seek_Camp(bot_state_t *bs, char *s); +void AIEnter_Battle_Fight(bot_state_t *bs, char *s); +void AIEnter_Battle_Chase(bot_state_t *bs, char *s); +void AIEnter_Battle_Retreat(bot_state_t *bs, char *s); +void AIEnter_Battle_NBG(bot_state_t *bs, char *s); +int AINode_Intermission(bot_state_t *bs); +int AINode_Observer(bot_state_t *bs); +int AINode_Respawn(bot_state_t *bs); +int AINode_Stand(bot_state_t *bs); +int AINode_Seek_ActivateEntity(bot_state_t *bs); +int AINode_Seek_NBG(bot_state_t *bs); +int AINode_Seek_LTG(bot_state_t *bs); +int AINode_Battle_Fight(bot_state_t *bs); +int AINode_Battle_Chase(bot_state_t *bs); +int AINode_Battle_Retreat(bot_state_t *bs); +int AINode_Battle_NBG(bot_state_t *bs); + +void BotResetNodeSwitches(void); +void BotDumpNodeSwitches(bot_state_t *bs); + diff --git a/reaction/engine/code/game/ai_dmq3.c b/reaction/engine/code/game/ai_dmq3.c new file mode 100644 index 00000000..894e295d --- /dev/null +++ b/reaction/engine/code/game/ai_dmq3.c @@ -0,0 +1,5460 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: ai_dmq3.c + * + * desc: Quake3 bot AI + * + * $Archive: /MissionPack/code/game/ai_dmq3.c $ + * + *****************************************************************************/ + + +#include "g_local.h" +#include "../botlib/botlib.h" +#include "../botlib/be_aas.h" +#include "../botlib/be_ea.h" +#include "../botlib/be_ai_char.h" +#include "../botlib/be_ai_chat.h" +#include "../botlib/be_ai_gen.h" +#include "../botlib/be_ai_goal.h" +#include "../botlib/be_ai_move.h" +#include "../botlib/be_ai_weap.h" +// +#include "ai_main.h" +#include "ai_dmq3.h" +#include "ai_chat.h" +#include "ai_cmd.h" +#include "ai_dmnet.h" +#include "ai_team.h" +// +#include "chars.h" //characteristics +#include "inv.h" //indexes into the inventory +#include "syn.h" //synonyms +#include "match.h" //string matching types and vars + +// for the voice chats +#include "../../ui/menudef.h" // sos001205 - for q3_ui also + +// from aasfile.h +#define AREACONTENTS_MOVER 1024 +#define AREACONTENTS_MODELNUMSHIFT 24 +#define AREACONTENTS_MAXMODELNUM 0xFF +#define AREACONTENTS_MODELNUM (AREACONTENTS_MAXMODELNUM << AREACONTENTS_MODELNUMSHIFT) + +#define IDEAL_ATTACKDIST 140 + +#define MAX_WAYPOINTS 128 +// +bot_waypoint_t botai_waypoints[MAX_WAYPOINTS]; +bot_waypoint_t *botai_freewaypoints; + +//NOTE: not using a cvars which can be updated because the game should be reloaded anyway +int gametype; //game type +int maxclients; //maximum number of clients + +vmCvar_t bot_grapple; +vmCvar_t bot_rocketjump; +vmCvar_t bot_fastchat; +vmCvar_t bot_nochat; +vmCvar_t bot_testrchat; +vmCvar_t bot_challenge; +vmCvar_t bot_predictobstacles; +vmCvar_t g_spSkill; + +extern vmCvar_t bot_developer; + +vec3_t lastteleport_origin; //last teleport event origin +float lastteleport_time; //last teleport event time +int max_bspmodelindex; //maximum BSP model index + +//CTF flag goals +bot_goal_t ctf_redflag; +bot_goal_t ctf_blueflag; +#ifdef MISSIONPACK +bot_goal_t ctf_neutralflag; +bot_goal_t redobelisk; +bot_goal_t blueobelisk; +bot_goal_t neutralobelisk; +#endif + +#define MAX_ALTROUTEGOALS 32 + +int altroutegoals_setup; +aas_altroutegoal_t red_altroutegoals[MAX_ALTROUTEGOALS]; +int red_numaltroutegoals; +aas_altroutegoal_t blue_altroutegoals[MAX_ALTROUTEGOALS]; +int blue_numaltroutegoals; + + +/* +================== +BotSetUserInfo +================== +*/ +void BotSetUserInfo(bot_state_t *bs, char *key, char *value) { + char userinfo[MAX_INFO_STRING]; + + trap_GetUserinfo(bs->client, userinfo, sizeof(userinfo)); + Info_SetValueForKey(userinfo, key, value); + trap_SetUserinfo(bs->client, userinfo); + ClientUserinfoChanged( bs->client ); +} + +/* +================== +BotCTFCarryingFlag +================== +*/ +int BotCTFCarryingFlag(bot_state_t *bs) { + if (gametype != GT_CTF) return CTF_FLAG_NONE; + + if (bs->inventory[INVENTORY_REDFLAG] > 0) return CTF_FLAG_RED; + else if (bs->inventory[INVENTORY_BLUEFLAG] > 0) return CTF_FLAG_BLUE; + return CTF_FLAG_NONE; +} + +/* +================== +BotTeam +================== +*/ +int BotTeam(bot_state_t *bs) { + char info[1024]; + + if (bs->client < 0 || bs->client >= MAX_CLIENTS) { + //BotAI_Print(PRT_ERROR, "BotCTFTeam: client out of range\n"); + return qfalse; + } + trap_GetConfigstring(CS_PLAYERS+bs->client, info, sizeof(info)); + // + if (atoi(Info_ValueForKey(info, "t")) == TEAM_RED) return TEAM_RED; + else if (atoi(Info_ValueForKey(info, "t")) == TEAM_BLUE) return TEAM_BLUE; + return TEAM_FREE; +} + +/* +================== +BotOppositeTeam +================== +*/ +int BotOppositeTeam(bot_state_t *bs) { + switch(BotTeam(bs)) { + case TEAM_RED: return TEAM_BLUE; + case TEAM_BLUE: return TEAM_RED; + default: return TEAM_FREE; + } +} + +/* +================== +BotEnemyFlag +================== +*/ +bot_goal_t *BotEnemyFlag(bot_state_t *bs) { + if (BotTeam(bs) == TEAM_RED) { + return &ctf_blueflag; + } + else { + return &ctf_redflag; + } +} + +/* +================== +BotTeamFlag +================== +*/ +bot_goal_t *BotTeamFlag(bot_state_t *bs) { + if (BotTeam(bs) == TEAM_RED) { + return &ctf_redflag; + } + else { + return &ctf_blueflag; + } +} + + +/* +================== +EntityIsDead +================== +*/ +qboolean EntityIsDead(aas_entityinfo_t *entinfo) { + playerState_t ps; + + if (entinfo->number >= 0 && entinfo->number < MAX_CLIENTS) { + //retrieve the current client state + BotAI_GetClientState( entinfo->number, &ps ); + if (ps.pm_type != PM_NORMAL) return qtrue; + } + return qfalse; +} + +/* +================== +EntityCarriesFlag +================== +*/ +qboolean EntityCarriesFlag(aas_entityinfo_t *entinfo) { + if ( entinfo->powerups & ( 1 << PW_REDFLAG ) ) + return qtrue; + if ( entinfo->powerups & ( 1 << PW_BLUEFLAG ) ) + return qtrue; +#ifdef MISSIONPACK + if ( entinfo->powerups & ( 1 << PW_NEUTRALFLAG ) ) + return qtrue; +#endif + return qfalse; +} + +/* +================== +EntityIsInvisible +================== +*/ +qboolean EntityIsInvisible(aas_entityinfo_t *entinfo) { + // the flag is always visible + if (EntityCarriesFlag(entinfo)) { + return qfalse; + } + if (entinfo->powerups & (1 << PW_INVIS)) { + return qtrue; + } + return qfalse; +} + +/* +================== +EntityIsShooting +================== +*/ +qboolean EntityIsShooting(aas_entityinfo_t *entinfo) { + if (entinfo->flags & EF_FIRING) { + return qtrue; + } + return qfalse; +} + +/* +================== +EntityIsChatting +================== +*/ +qboolean EntityIsChatting(aas_entityinfo_t *entinfo) { + if (entinfo->flags & EF_TALK) { + return qtrue; + } + return qfalse; +} + +/* +================== +EntityHasQuad +================== +*/ +qboolean EntityHasQuad(aas_entityinfo_t *entinfo) { + if (entinfo->powerups & (1 << PW_QUAD)) { + return qtrue; + } + return qfalse; +} + +#ifdef MISSIONPACK +/* +================== +EntityHasKamikze +================== +*/ +qboolean EntityHasKamikaze(aas_entityinfo_t *entinfo) { + if (entinfo->flags & EF_KAMIKAZE) { + return qtrue; + } + return qfalse; +} + +/* +================== +EntityCarriesCubes +================== +*/ +qboolean EntityCarriesCubes(aas_entityinfo_t *entinfo) { + entityState_t state; + + if (gametype != GT_HARVESTER) + return qfalse; + //FIXME: get this info from the aas_entityinfo_t ? + BotAI_GetEntityState(entinfo->number, &state); + if (state.generic1 > 0) + return qtrue; + return qfalse; +} + +/* +================== +Bot1FCTFCarryingFlag +================== +*/ +int Bot1FCTFCarryingFlag(bot_state_t *bs) { + if (gametype != GT_1FCTF) return qfalse; + + if (bs->inventory[INVENTORY_NEUTRALFLAG] > 0) return qtrue; + return qfalse; +} + +/* +================== +BotHarvesterCarryingCubes +================== +*/ +int BotHarvesterCarryingCubes(bot_state_t *bs) { + if (gametype != GT_HARVESTER) return qfalse; + + if (bs->inventory[INVENTORY_REDCUBE] > 0) return qtrue; + if (bs->inventory[INVENTORY_BLUECUBE] > 0) return qtrue; + return qfalse; +} +#endif + +/* +================== +BotRememberLastOrderedTask +================== +*/ +void BotRememberLastOrderedTask(bot_state_t *bs) { + if (!bs->ordered) { + return; + } + bs->lastgoal_decisionmaker = bs->decisionmaker; + bs->lastgoal_ltgtype = bs->ltgtype; + memcpy(&bs->lastgoal_teamgoal, &bs->teamgoal, sizeof(bot_goal_t)); + bs->lastgoal_teammate = bs->teammate; +} + +/* +================== +BotSetTeamStatus +================== +*/ +void BotSetTeamStatus(bot_state_t *bs) { +#ifdef MISSIONPACK + int teamtask; + aas_entityinfo_t entinfo; + + teamtask = TEAMTASK_PATROL; + + switch(bs->ltgtype) { + case LTG_TEAMHELP: + break; + case LTG_TEAMACCOMPANY: + BotEntityInfo(bs->teammate, &entinfo); + if ( ( (gametype == GT_CTF || gametype == GT_1FCTF) && EntityCarriesFlag(&entinfo)) + || ( gametype == GT_HARVESTER && EntityCarriesCubes(&entinfo)) ) { + teamtask = TEAMTASK_ESCORT; + } + else { + teamtask = TEAMTASK_FOLLOW; + } + break; + case LTG_DEFENDKEYAREA: + teamtask = TEAMTASK_DEFENSE; + break; + case LTG_GETFLAG: + teamtask = TEAMTASK_OFFENSE; + break; + case LTG_RUSHBASE: + teamtask = TEAMTASK_DEFENSE; + break; + case LTG_RETURNFLAG: + teamtask = TEAMTASK_RETRIEVE; + break; + case LTG_CAMP: + case LTG_CAMPORDER: + teamtask = TEAMTASK_CAMP; + break; + case LTG_PATROL: + teamtask = TEAMTASK_PATROL; + break; + case LTG_GETITEM: + teamtask = TEAMTASK_PATROL; + break; + case LTG_KILL: + teamtask = TEAMTASK_PATROL; + break; + case LTG_HARVEST: + teamtask = TEAMTASK_OFFENSE; + break; + case LTG_ATTACKENEMYBASE: + teamtask = TEAMTASK_OFFENSE; + break; + default: + teamtask = TEAMTASK_PATROL; + break; + } + BotSetUserInfo(bs, "teamtask", va("%d", teamtask)); +#endif +} + +/* +================== +BotSetLastOrderedTask +================== +*/ +int BotSetLastOrderedTask(bot_state_t *bs) { + + if (gametype == GT_CTF) { + // don't go back to returning the flag if it's at the base + if ( bs->lastgoal_ltgtype == LTG_RETURNFLAG ) { + if ( BotTeam(bs) == TEAM_RED ) { + if ( bs->redflagstatus == 0 ) { + bs->lastgoal_ltgtype = 0; + } + } + else { + if ( bs->blueflagstatus == 0 ) { + bs->lastgoal_ltgtype = 0; + } + } + } + } + + if ( bs->lastgoal_ltgtype ) { + bs->decisionmaker = bs->lastgoal_decisionmaker; + bs->ordered = qtrue; + bs->ltgtype = bs->lastgoal_ltgtype; + memcpy(&bs->teamgoal, &bs->lastgoal_teamgoal, sizeof(bot_goal_t)); + bs->teammate = bs->lastgoal_teammate; + bs->teamgoal_time = FloatTime() + 300; + BotSetTeamStatus(bs); + // + if ( gametype == GT_CTF ) { + if ( bs->ltgtype == LTG_GETFLAG ) { + bot_goal_t *tb, *eb; + int tt, et; + + tb = BotTeamFlag(bs); + eb = BotEnemyFlag(bs); + tt = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, tb->areanum, TFL_DEFAULT); + et = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, eb->areanum, TFL_DEFAULT); + // if the travel time towards the enemy base is larger than towards our base + if (et > tt) { + //get an alternative route goal towards the enemy base + BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); + } + } + } + return qtrue; + } + return qfalse; +} + +/* +================== +BotRefuseOrder +================== +*/ +void BotRefuseOrder(bot_state_t *bs) { + if (!bs->ordered) + return; + // if the bot was ordered to do something + if ( bs->order_time && bs->order_time > FloatTime() - 10 ) { + trap_EA_Action(bs->client, ACTION_NEGATIVE); + BotVoiceChat(bs, bs->decisionmaker, VOICECHAT_NO); + bs->order_time = 0; + } +} + +/* +================== +BotCTFSeekGoals +================== +*/ +void BotCTFSeekGoals(bot_state_t *bs) { + float rnd, l1, l2; + int flagstatus, c; + vec3_t dir; + aas_entityinfo_t entinfo; + + //when carrying a flag in ctf the bot should rush to the base + if (BotCTFCarryingFlag(bs)) { + //if not already rushing to the base + if (bs->ltgtype != LTG_RUSHBASE) { + BotRefuseOrder(bs); + bs->ltgtype = LTG_RUSHBASE; + bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; + bs->rushbaseaway_time = 0; + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + // + switch(BotTeam(bs)) { + case TEAM_RED: VectorSubtract(bs->origin, ctf_blueflag.origin, dir); break; + case TEAM_BLUE: VectorSubtract(bs->origin, ctf_redflag.origin, dir); break; + default: VectorSet(dir, 999, 999, 999); break; + } + // if the bot picked up the flag very close to the enemy base + if ( VectorLength(dir) < 128 ) { + // get an alternative route goal through the enemy base + BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); + } else { + // don't use any alt route goal, just get the hell out of the base + bs->altroutegoal.areanum = 0; + } + BotSetUserInfo(bs, "teamtask", va("%d", TEAMTASK_OFFENSE)); + BotVoiceChat(bs, -1, VOICECHAT_IHAVEFLAG); + } + else if (bs->rushbaseaway_time > FloatTime()) { + if (BotTeam(bs) == TEAM_RED) flagstatus = bs->redflagstatus; + else flagstatus = bs->blueflagstatus; + //if the flag is back + if (flagstatus == 0) { + bs->rushbaseaway_time = 0; + } + } + return; + } + // if the bot decided to follow someone + if ( bs->ltgtype == LTG_TEAMACCOMPANY && !bs->ordered ) { + // if the team mate being accompanied no longer carries the flag + BotEntityInfo(bs->teammate, &entinfo); + if (!EntityCarriesFlag(&entinfo)) { + bs->ltgtype = 0; + } + } + // + if (BotTeam(bs) == TEAM_RED) flagstatus = bs->redflagstatus * 2 + bs->blueflagstatus; + else flagstatus = bs->blueflagstatus * 2 + bs->redflagstatus; + //if our team has the enemy flag and our flag is at the base + if (flagstatus == 1) { + // + if (bs->owndecision_time < FloatTime()) { + //if Not defending the base already + if (!(bs->ltgtype == LTG_DEFENDKEYAREA && + (bs->teamgoal.number == ctf_redflag.number || + bs->teamgoal.number == ctf_blueflag.number))) { + //if there is a visible team mate flag carrier + c = BotTeamFlagCarrierVisible(bs); + if (c >= 0 && + // and not already following the team mate flag carrier + (bs->ltgtype != LTG_TEAMACCOMPANY || bs->teammate != c)) { + // + BotRefuseOrder(bs); + //follow the flag carrier + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + //the team mate + bs->teammate = c; + //last time the team mate was visible + bs->teammatevisible_time = FloatTime(); + //no message + bs->teammessage_time = 0; + //no arrive message + bs->arrive_time = 1; + // + BotVoiceChat(bs, bs->teammate, VOICECHAT_ONFOLLOW); + //get the team goal time + bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; + bs->ltgtype = LTG_TEAMACCOMPANY; + bs->formation_dist = 3.5 * 32; //3.5 meter + BotSetTeamStatus(bs); + bs->owndecision_time = FloatTime() + 5; + } + } + } + return; + } + //if the enemy has our flag + else if (flagstatus == 2) { + // + if (bs->owndecision_time < FloatTime()) { + //if enemy flag carrier is visible + c = BotEnemyFlagCarrierVisible(bs); + if (c >= 0) { + //FIXME: fight enemy flag carrier + } + //if not already doing something important + if (bs->ltgtype != LTG_GETFLAG && + bs->ltgtype != LTG_RETURNFLAG && + bs->ltgtype != LTG_TEAMHELP && + bs->ltgtype != LTG_TEAMACCOMPANY && + bs->ltgtype != LTG_CAMPORDER && + bs->ltgtype != LTG_PATROL && + bs->ltgtype != LTG_GETITEM) { + + BotRefuseOrder(bs); + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + // + if (random() < 0.5) { + //go for the enemy flag + bs->ltgtype = LTG_GETFLAG; + } + else { + bs->ltgtype = LTG_RETURNFLAG; + } + //no team message + bs->teammessage_time = 0; + //set the time the bot will stop getting the flag + bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME; + //get an alternative route goal towards the enemy base + BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); + // + BotSetTeamStatus(bs); + bs->owndecision_time = FloatTime() + 5; + } + } + return; + } + //if both flags Not at their bases + else if (flagstatus == 3) { + // + if (bs->owndecision_time < FloatTime()) { + // if not trying to return the flag and not following the team flag carrier + if ( bs->ltgtype != LTG_RETURNFLAG && bs->ltgtype != LTG_TEAMACCOMPANY ) { + // + c = BotTeamFlagCarrierVisible(bs); + // if there is a visible team mate flag carrier + if (c >= 0) { + BotRefuseOrder(bs); + //follow the flag carrier + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + //the team mate + bs->teammate = c; + //last time the team mate was visible + bs->teammatevisible_time = FloatTime(); + //no message + bs->teammessage_time = 0; + //no arrive message + bs->arrive_time = 1; + // + BotVoiceChat(bs, bs->teammate, VOICECHAT_ONFOLLOW); + //get the team goal time + bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; + bs->ltgtype = LTG_TEAMACCOMPANY; + bs->formation_dist = 3.5 * 32; //3.5 meter + // + BotSetTeamStatus(bs); + bs->owndecision_time = FloatTime() + 5; + } + else { + BotRefuseOrder(bs); + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + //get the enemy flag + bs->teammessage_time = FloatTime() + 2 * random(); + //get the flag + bs->ltgtype = LTG_RETURNFLAG; + //set the time the bot will stop getting the flag + bs->teamgoal_time = FloatTime() + CTF_RETURNFLAG_TIME; + //get an alternative route goal towards the enemy base + BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); + // + BotSetTeamStatus(bs); + bs->owndecision_time = FloatTime() + 5; + } + } + } + return; + } + // don't just do something wait for the bot team leader to give orders + if (BotTeamLeader(bs)) { + return; + } + // if the bot is ordered to do something + if ( bs->lastgoal_ltgtype ) { + bs->teamgoal_time += 60; + } + // if the bot decided to do something on it's own and has a last ordered goal + if ( !bs->ordered && bs->lastgoal_ltgtype ) { + bs->ltgtype = 0; + } + //if already a CTF or team goal + if (bs->ltgtype == LTG_TEAMHELP || + bs->ltgtype == LTG_TEAMACCOMPANY || + bs->ltgtype == LTG_DEFENDKEYAREA || + bs->ltgtype == LTG_GETFLAG || + bs->ltgtype == LTG_RUSHBASE || + bs->ltgtype == LTG_RETURNFLAG || + bs->ltgtype == LTG_CAMPORDER || + bs->ltgtype == LTG_PATROL || + bs->ltgtype == LTG_GETITEM || + bs->ltgtype == LTG_MAKELOVE_UNDER || + bs->ltgtype == LTG_MAKELOVE_ONTOP) { + return; + } + // + if (BotSetLastOrderedTask(bs)) + return; + // + if (bs->owndecision_time > FloatTime()) + return;; + //if the bot is roaming + if (bs->ctfroam_time > FloatTime()) + return; + //if the bot has anough aggression to decide what to do + if (BotAggression(bs) < 50) + return; + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + // + if (bs->teamtaskpreference & (TEAMTP_ATTACKER|TEAMTP_DEFENDER)) { + if (bs->teamtaskpreference & TEAMTP_ATTACKER) { + l1 = 0.7f; + } + else { + l1 = 0.2f; + } + l2 = 0.9f; + } + else { + l1 = 0.4f; + l2 = 0.7f; + } + //get the flag or defend the base + rnd = random(); + if (rnd < l1 && ctf_redflag.areanum && ctf_blueflag.areanum) { + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + bs->ltgtype = LTG_GETFLAG; + //set the time the bot will stop getting the flag + bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME; + //get an alternative route goal towards the enemy base + BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); + BotSetTeamStatus(bs); + } + else if (rnd < l2 && ctf_redflag.areanum && ctf_blueflag.areanum) { + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + // + if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &ctf_redflag, sizeof(bot_goal_t)); + else memcpy(&bs->teamgoal, &ctf_blueflag, sizeof(bot_goal_t)); + //set the ltg type + bs->ltgtype = LTG_DEFENDKEYAREA; + //set the time the bot stops defending the base + bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; + bs->defendaway_time = 0; + BotSetTeamStatus(bs); + } + else { + bs->ltgtype = 0; + //set the time the bot will stop roaming + bs->ctfroam_time = FloatTime() + CTF_ROAM_TIME; + BotSetTeamStatus(bs); + } + bs->owndecision_time = FloatTime() + 5; +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotCTFRetreatGoals +================== +*/ +void BotCTFRetreatGoals(bot_state_t *bs) { + //when carrying a flag in ctf the bot should rush to the base + if (BotCTFCarryingFlag(bs)) { + //if not already rushing to the base + if (bs->ltgtype != LTG_RUSHBASE) { + BotRefuseOrder(bs); + bs->ltgtype = LTG_RUSHBASE; + bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; + bs->rushbaseaway_time = 0; + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + BotSetTeamStatus(bs); + } + } +} + +#ifdef MISSIONPACK +/* +================== +Bot1FCTFSeekGoals +================== +*/ +void Bot1FCTFSeekGoals(bot_state_t *bs) { + aas_entityinfo_t entinfo; + float rnd, l1, l2; + int c; + + //when carrying a flag in ctf the bot should rush to the base + if (Bot1FCTFCarryingFlag(bs)) { + //if not already rushing to the base + if (bs->ltgtype != LTG_RUSHBASE) { + BotRefuseOrder(bs); + bs->ltgtype = LTG_RUSHBASE; + bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; + bs->rushbaseaway_time = 0; + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + //get an alternative route goal towards the enemy base + BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); + // + BotSetTeamStatus(bs); + BotVoiceChat(bs, -1, VOICECHAT_IHAVEFLAG); + } + return; + } + // if the bot decided to follow someone + if ( bs->ltgtype == LTG_TEAMACCOMPANY && !bs->ordered ) { + // if the team mate being accompanied no longer carries the flag + BotEntityInfo(bs->teammate, &entinfo); + if (!EntityCarriesFlag(&entinfo)) { + bs->ltgtype = 0; + } + } + //our team has the flag + if (bs->neutralflagstatus == 1) { + if (bs->owndecision_time < FloatTime()) { + // if not already following someone + if (bs->ltgtype != LTG_TEAMACCOMPANY) { + //if there is a visible team mate flag carrier + c = BotTeamFlagCarrierVisible(bs); + if (c >= 0) { + BotRefuseOrder(bs); + //follow the flag carrier + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + //the team mate + bs->teammate = c; + //last time the team mate was visible + bs->teammatevisible_time = FloatTime(); + //no message + bs->teammessage_time = 0; + //no arrive message + bs->arrive_time = 1; + // + BotVoiceChat(bs, bs->teammate, VOICECHAT_ONFOLLOW); + //get the team goal time + bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; + bs->ltgtype = LTG_TEAMACCOMPANY; + bs->formation_dist = 3.5 * 32; //3.5 meter + BotSetTeamStatus(bs); + bs->owndecision_time = FloatTime() + 5; + return; + } + } + //if already a CTF or team goal + if (bs->ltgtype == LTG_TEAMHELP || + bs->ltgtype == LTG_TEAMACCOMPANY || + bs->ltgtype == LTG_DEFENDKEYAREA || + bs->ltgtype == LTG_GETFLAG || + bs->ltgtype == LTG_RUSHBASE || + bs->ltgtype == LTG_CAMPORDER || + bs->ltgtype == LTG_PATROL || + bs->ltgtype == LTG_ATTACKENEMYBASE || + bs->ltgtype == LTG_GETITEM || + bs->ltgtype == LTG_MAKELOVE_UNDER || + bs->ltgtype == LTG_MAKELOVE_ONTOP) { + return; + } + //if not already attacking the enemy base + if (bs->ltgtype != LTG_ATTACKENEMYBASE) { + BotRefuseOrder(bs); + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + // + if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &ctf_blueflag, sizeof(bot_goal_t)); + else memcpy(&bs->teamgoal, &ctf_redflag, sizeof(bot_goal_t)); + //set the ltg type + bs->ltgtype = LTG_ATTACKENEMYBASE; + //set the time the bot will stop getting the flag + bs->teamgoal_time = FloatTime() + TEAM_ATTACKENEMYBASE_TIME; + BotSetTeamStatus(bs); + bs->owndecision_time = FloatTime() + 5; + } + } + return; + } + //enemy team has the flag + else if (bs->neutralflagstatus == 2) { + if (bs->owndecision_time < FloatTime()) { + c = BotEnemyFlagCarrierVisible(bs); + if (c >= 0) { + //FIXME: attack enemy flag carrier + } + //if already a CTF or team goal + if (bs->ltgtype == LTG_TEAMHELP || + bs->ltgtype == LTG_TEAMACCOMPANY || + bs->ltgtype == LTG_CAMPORDER || + bs->ltgtype == LTG_PATROL || + bs->ltgtype == LTG_GETITEM) { + return; + } + // if not already defending the base + if (bs->ltgtype != LTG_DEFENDKEYAREA) { + BotRefuseOrder(bs); + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + // + if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &ctf_redflag, sizeof(bot_goal_t)); + else memcpy(&bs->teamgoal, &ctf_blueflag, sizeof(bot_goal_t)); + //set the ltg type + bs->ltgtype = LTG_DEFENDKEYAREA; + //set the time the bot stops defending the base + bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; + bs->defendaway_time = 0; + BotSetTeamStatus(bs); + bs->owndecision_time = FloatTime() + 5; + } + } + return; + } + // don't just do something wait for the bot team leader to give orders + if (BotTeamLeader(bs)) { + return; + } + // if the bot is ordered to do something + if ( bs->lastgoal_ltgtype ) { + bs->teamgoal_time += 60; + } + // if the bot decided to do something on it's own and has a last ordered goal + if ( !bs->ordered && bs->lastgoal_ltgtype ) { + bs->ltgtype = 0; + } + //if already a CTF or team goal + if (bs->ltgtype == LTG_TEAMHELP || + bs->ltgtype == LTG_TEAMACCOMPANY || + bs->ltgtype == LTG_DEFENDKEYAREA || + bs->ltgtype == LTG_GETFLAG || + bs->ltgtype == LTG_RUSHBASE || + bs->ltgtype == LTG_RETURNFLAG || + bs->ltgtype == LTG_CAMPORDER || + bs->ltgtype == LTG_PATROL || + bs->ltgtype == LTG_ATTACKENEMYBASE || + bs->ltgtype == LTG_GETITEM || + bs->ltgtype == LTG_MAKELOVE_UNDER || + bs->ltgtype == LTG_MAKELOVE_ONTOP) { + return; + } + // + if (BotSetLastOrderedTask(bs)) + return; + // + if (bs->owndecision_time > FloatTime()) + return;; + //if the bot is roaming + if (bs->ctfroam_time > FloatTime()) + return; + //if the bot has anough aggression to decide what to do + if (BotAggression(bs) < 50) + return; + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + // + if (bs->teamtaskpreference & (TEAMTP_ATTACKER|TEAMTP_DEFENDER)) { + if (bs->teamtaskpreference & TEAMTP_ATTACKER) { + l1 = 0.7f; + } + else { + l1 = 0.2f; + } + l2 = 0.9f; + } + else { + l1 = 0.4f; + l2 = 0.7f; + } + //get the flag or defend the base + rnd = random(); + if (rnd < l1 && ctf_neutralflag.areanum) { + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + bs->ltgtype = LTG_GETFLAG; + //set the time the bot will stop getting the flag + bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME; + BotSetTeamStatus(bs); + } + else if (rnd < l2 && ctf_redflag.areanum && ctf_blueflag.areanum) { + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + // + if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &ctf_redflag, sizeof(bot_goal_t)); + else memcpy(&bs->teamgoal, &ctf_blueflag, sizeof(bot_goal_t)); + //set the ltg type + bs->ltgtype = LTG_DEFENDKEYAREA; + //set the time the bot stops defending the base + bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; + bs->defendaway_time = 0; + BotSetTeamStatus(bs); + } + else { + bs->ltgtype = 0; + //set the time the bot will stop roaming + bs->ctfroam_time = FloatTime() + CTF_ROAM_TIME; + BotSetTeamStatus(bs); + } + bs->owndecision_time = FloatTime() + 5; +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +Bot1FCTFRetreatGoals +================== +*/ +void Bot1FCTFRetreatGoals(bot_state_t *bs) { + //when carrying a flag in ctf the bot should rush to the enemy base + if (Bot1FCTFCarryingFlag(bs)) { + //if not already rushing to the base + if (bs->ltgtype != LTG_RUSHBASE) { + BotRefuseOrder(bs); + bs->ltgtype = LTG_RUSHBASE; + bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; + bs->rushbaseaway_time = 0; + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + //get an alternative route goal towards the enemy base + BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); + BotSetTeamStatus(bs); + } + } +} + +/* +================== +BotObeliskSeekGoals +================== +*/ +void BotObeliskSeekGoals(bot_state_t *bs) { + float rnd, l1, l2; + + // don't just do something wait for the bot team leader to give orders + if (BotTeamLeader(bs)) { + return; + } + // if the bot is ordered to do something + if ( bs->lastgoal_ltgtype ) { + bs->teamgoal_time += 60; + } + //if already a team goal + if (bs->ltgtype == LTG_TEAMHELP || + bs->ltgtype == LTG_TEAMACCOMPANY || + bs->ltgtype == LTG_DEFENDKEYAREA || + bs->ltgtype == LTG_GETFLAG || + bs->ltgtype == LTG_RUSHBASE || + bs->ltgtype == LTG_RETURNFLAG || + bs->ltgtype == LTG_CAMPORDER || + bs->ltgtype == LTG_PATROL || + bs->ltgtype == LTG_ATTACKENEMYBASE || + bs->ltgtype == LTG_GETITEM || + bs->ltgtype == LTG_MAKELOVE_UNDER || + bs->ltgtype == LTG_MAKELOVE_ONTOP) { + return; + } + // + if (BotSetLastOrderedTask(bs)) + return; + //if the bot is roaming + if (bs->ctfroam_time > FloatTime()) + return; + //if the bot has anough aggression to decide what to do + if (BotAggression(bs) < 50) + return; + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + // + if (bs->teamtaskpreference & (TEAMTP_ATTACKER|TEAMTP_DEFENDER)) { + if (bs->teamtaskpreference & TEAMTP_ATTACKER) { + l1 = 0.7f; + } + else { + l1 = 0.2f; + } + l2 = 0.9f; + } + else { + l1 = 0.4f; + l2 = 0.7f; + } + //get the flag or defend the base + rnd = random(); + if (rnd < l1 && redobelisk.areanum && blueobelisk.areanum) { + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + // + if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &blueobelisk, sizeof(bot_goal_t)); + else memcpy(&bs->teamgoal, &redobelisk, sizeof(bot_goal_t)); + //set the ltg type + bs->ltgtype = LTG_ATTACKENEMYBASE; + //set the time the bot will stop attacking the enemy base + bs->teamgoal_time = FloatTime() + TEAM_ATTACKENEMYBASE_TIME; + //get an alternate route goal towards the enemy base + BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); + BotSetTeamStatus(bs); + } + else if (rnd < l2 && redobelisk.areanum && blueobelisk.areanum) { + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + // + if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &redobelisk, sizeof(bot_goal_t)); + else memcpy(&bs->teamgoal, &blueobelisk, sizeof(bot_goal_t)); + //set the ltg type + bs->ltgtype = LTG_DEFENDKEYAREA; + //set the time the bot stops defending the base + bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; + bs->defendaway_time = 0; + BotSetTeamStatus(bs); + } + else { + bs->ltgtype = 0; + //set the time the bot will stop roaming + bs->ctfroam_time = FloatTime() + CTF_ROAM_TIME; + BotSetTeamStatus(bs); + } +} + +/* +================== +BotGoHarvest +================== +*/ +void BotGoHarvest(bot_state_t *bs) { + // + if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &blueobelisk, sizeof(bot_goal_t)); + else memcpy(&bs->teamgoal, &redobelisk, sizeof(bot_goal_t)); + //set the ltg type + bs->ltgtype = LTG_HARVEST; + //set the time the bot will stop harvesting + bs->teamgoal_time = FloatTime() + TEAM_HARVEST_TIME; + bs->harvestaway_time = 0; + BotSetTeamStatus(bs); +} + +/* +================== +BotObeliskRetreatGoals +================== +*/ +void BotObeliskRetreatGoals(bot_state_t *bs) { + //nothing special +} + +/* +================== +BotHarvesterSeekGoals +================== +*/ +void BotHarvesterSeekGoals(bot_state_t *bs) { + aas_entityinfo_t entinfo; + float rnd, l1, l2; + int c; + + //when carrying cubes in harvester the bot should rush to the base + if (BotHarvesterCarryingCubes(bs)) { + //if not already rushing to the base + if (bs->ltgtype != LTG_RUSHBASE) { + BotRefuseOrder(bs); + bs->ltgtype = LTG_RUSHBASE; + bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; + bs->rushbaseaway_time = 0; + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + //get an alternative route goal towards the enemy base + BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); + // + BotSetTeamStatus(bs); + } + return; + } + // don't just do something wait for the bot team leader to give orders + if (BotTeamLeader(bs)) { + return; + } + // if the bot decided to follow someone + if ( bs->ltgtype == LTG_TEAMACCOMPANY && !bs->ordered ) { + // if the team mate being accompanied no longer carries the flag + BotEntityInfo(bs->teammate, &entinfo); + if (!EntityCarriesCubes(&entinfo)) { + bs->ltgtype = 0; + } + } + // if the bot is ordered to do something + if ( bs->lastgoal_ltgtype ) { + bs->teamgoal_time += 60; + } + //if not yet doing something + if (bs->ltgtype == LTG_TEAMHELP || + bs->ltgtype == LTG_TEAMACCOMPANY || + bs->ltgtype == LTG_DEFENDKEYAREA || + bs->ltgtype == LTG_GETFLAG || + bs->ltgtype == LTG_CAMPORDER || + bs->ltgtype == LTG_PATROL || + bs->ltgtype == LTG_ATTACKENEMYBASE || + bs->ltgtype == LTG_HARVEST || + bs->ltgtype == LTG_GETITEM || + bs->ltgtype == LTG_MAKELOVE_UNDER || + bs->ltgtype == LTG_MAKELOVE_ONTOP) { + return; + } + // + if (BotSetLastOrderedTask(bs)) + return; + //if the bot is roaming + if (bs->ctfroam_time > FloatTime()) + return; + //if the bot has anough aggression to decide what to do + if (BotAggression(bs) < 50) + return; + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + // + c = BotEnemyCubeCarrierVisible(bs); + if (c >= 0) { + //FIXME: attack enemy cube carrier + } + if (bs->ltgtype != LTG_TEAMACCOMPANY) { + //if there is a visible team mate carrying cubes + c = BotTeamCubeCarrierVisible(bs); + if (c >= 0) { + //follow the team mate carrying cubes + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + //the team mate + bs->teammate = c; + //last time the team mate was visible + bs->teammatevisible_time = FloatTime(); + //no message + bs->teammessage_time = 0; + //no arrive message + bs->arrive_time = 1; + // + BotVoiceChat(bs, bs->teammate, VOICECHAT_ONFOLLOW); + //get the team goal time + bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; + bs->ltgtype = LTG_TEAMACCOMPANY; + bs->formation_dist = 3.5 * 32; //3.5 meter + BotSetTeamStatus(bs); + return; + } + } + // + if (bs->teamtaskpreference & (TEAMTP_ATTACKER|TEAMTP_DEFENDER)) { + if (bs->teamtaskpreference & TEAMTP_ATTACKER) { + l1 = 0.7f; + } + else { + l1 = 0.2f; + } + l2 = 0.9f; + } + else { + l1 = 0.4f; + l2 = 0.7f; + } + // + rnd = random(); + if (rnd < l1 && redobelisk.areanum && blueobelisk.areanum) { + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + BotGoHarvest(bs); + } + else if (rnd < l2 && redobelisk.areanum && blueobelisk.areanum) { + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + // + if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &redobelisk, sizeof(bot_goal_t)); + else memcpy(&bs->teamgoal, &blueobelisk, sizeof(bot_goal_t)); + //set the ltg type + bs->ltgtype = LTG_DEFENDKEYAREA; + //set the time the bot stops defending the base + bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; + bs->defendaway_time = 0; + BotSetTeamStatus(bs); + } + else { + bs->ltgtype = 0; + //set the time the bot will stop roaming + bs->ctfroam_time = FloatTime() + CTF_ROAM_TIME; + BotSetTeamStatus(bs); + } +} + +/* +================== +BotHarvesterRetreatGoals +================== +*/ +void BotHarvesterRetreatGoals(bot_state_t *bs) { + //when carrying cubes in harvester the bot should rush to the base + if (BotHarvesterCarryingCubes(bs)) { + //if not already rushing to the base + if (bs->ltgtype != LTG_RUSHBASE) { + BotRefuseOrder(bs); + bs->ltgtype = LTG_RUSHBASE; + bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; + bs->rushbaseaway_time = 0; + bs->decisionmaker = bs->client; + bs->ordered = qfalse; + BotSetTeamStatus(bs); + } + return; + } +} +#endif + +/* +================== +BotTeamGoals +================== +*/ +void BotTeamGoals(bot_state_t *bs, int retreat) { + + if ( retreat ) { + if (gametype == GT_CTF) { + BotCTFRetreatGoals(bs); + } +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF) { + Bot1FCTFRetreatGoals(bs); + } + else if (gametype == GT_OBELISK) { + BotObeliskRetreatGoals(bs); + } + else if (gametype == GT_HARVESTER) { + BotHarvesterRetreatGoals(bs); + } +#endif + } + else { + if (gametype == GT_CTF) { + //decide what to do in CTF mode + BotCTFSeekGoals(bs); + } +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF) { + Bot1FCTFSeekGoals(bs); + } + else if (gametype == GT_OBELISK) { + BotObeliskSeekGoals(bs); + } + else if (gametype == GT_HARVESTER) { + BotHarvesterSeekGoals(bs); + } +#endif + } + // reset the order time which is used to see if + // we decided to refuse an order + bs->order_time = 0; +} + +/* +================== +BotPointAreaNum +================== +*/ +int BotPointAreaNum(vec3_t origin) { + int areanum, numareas, areas[10]; + vec3_t end; + + areanum = trap_AAS_PointAreaNum(origin); + if (areanum) return areanum; + VectorCopy(origin, end); + end[2] += 10; + numareas = trap_AAS_TraceAreas(origin, end, areas, NULL, 10); + if (numareas > 0) return areas[0]; + return 0; +} + +/* +================== +ClientName +================== +*/ +char *ClientName(int client, char *name, int size) { + char buf[MAX_INFO_STRING]; + + if (client < 0 || client >= MAX_CLIENTS) { + BotAI_Print(PRT_ERROR, "ClientName: client out of range\n"); + return "[client out of range]"; + } + trap_GetConfigstring(CS_PLAYERS+client, buf, sizeof(buf)); + strncpy(name, Info_ValueForKey(buf, "n"), size-1); + name[size-1] = '\0'; + Q_CleanStr( name ); + return name; +} + +/* +================== +ClientSkin +================== +*/ +char *ClientSkin(int client, char *skin, int size) { + char buf[MAX_INFO_STRING]; + + if (client < 0 || client >= MAX_CLIENTS) { + BotAI_Print(PRT_ERROR, "ClientSkin: client out of range\n"); + return "[client out of range]"; + } + trap_GetConfigstring(CS_PLAYERS+client, buf, sizeof(buf)); + strncpy(skin, Info_ValueForKey(buf, "model"), size-1); + skin[size-1] = '\0'; + return skin; +} + +/* +================== +ClientFromName +================== +*/ +int ClientFromName(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++) { + trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); + Q_CleanStr( buf ); + if (!Q_stricmp(Info_ValueForKey(buf, "n"), name)) return i; + } + return -1; +} + +/* +================== +ClientOnSameTeamFromName +================== +*/ +int ClientOnSameTeamFromName(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; + trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); + Q_CleanStr( buf ); + if (!Q_stricmp(Info_ValueForKey(buf, "n"), name)) return i; + } + return -1; +} + +/* +================== +stristr +================== +*/ +char *stristr(char *str, char *charset) { + int i; + + while(*str) { + for (i = 0; charset[i] && str[i]; i++) { + if (toupper(charset[i]) != toupper(str[i])) break; + } + if (!charset[i]) return str; + str++; + } + return NULL; +} + +/* +================== +EasyClientName +================== +*/ +char *EasyClientName(int client, char *buf, int size) { + int i; + char *str1, *str2, *ptr, c; + char name[128]; + + strcpy(name, ClientName(client, name, sizeof(name))); + for (i = 0; name[i]; i++) name[i] &= 127; + //remove all spaces + for (ptr = strstr(name, " "); ptr; ptr = strstr(name, " ")) { + memmove(ptr, ptr+1, strlen(ptr+1)+1); + } + //check for [x] and ]x[ clan names + str1 = strstr(name, "["); + str2 = strstr(name, "]"); + if (str1 && str2) { + if (str2 > str1) memmove(str1, str2+1, strlen(str2+1)+1); + else memmove(str2, str1+1, strlen(str1+1)+1); + } + //remove Mr prefix + if ((name[0] == 'm' || name[0] == 'M') && + (name[1] == 'r' || name[1] == 'R')) { + memmove(name, name+2, strlen(name+2)+1); + } + //only allow lower case alphabet characters + ptr = name; + while(*ptr) { + c = *ptr; + if ((c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || c == '_') { + ptr++; + } + else if (c >= 'A' && c <= 'Z') { + *ptr += 'a' - 'A'; + ptr++; + } + else { + memmove(ptr, ptr+1, strlen(ptr + 1)+1); + } + } + strncpy(buf, name, size-1); + buf[size-1] = '\0'; + return buf; +} + +/* +================== +BotSynonymContext +================== +*/ +int BotSynonymContext(bot_state_t *bs) { + int context; + + context = CONTEXT_NORMAL|CONTEXT_NEARBYITEM|CONTEXT_NAMES; + // + if (gametype == GT_CTF +#ifdef MISSIONPACK + || gametype == GT_1FCTF +#endif + ) { + if (BotTeam(bs) == TEAM_RED) context |= CONTEXT_CTFREDTEAM; + else context |= CONTEXT_CTFBLUETEAM; + } +#ifdef MISSIONPACK + else if (gametype == GT_OBELISK) { + if (BotTeam(bs) == TEAM_RED) context |= CONTEXT_OBELISKREDTEAM; + else context |= CONTEXT_OBELISKBLUETEAM; + } + else if (gametype == GT_HARVESTER) { + if (BotTeam(bs) == TEAM_RED) context |= CONTEXT_HARVESTERREDTEAM; + else context |= CONTEXT_HARVESTERBLUETEAM; + } +#endif + return context; +} + +/* +================== +BotChooseWeapon +================== +*/ +void BotChooseWeapon(bot_state_t *bs) { + int newweaponnum; + + if (bs->cur_ps.weaponstate == WEAPON_RAISING || + bs->cur_ps.weaponstate == WEAPON_DROPPING) { + trap_EA_SelectWeapon(bs->client, bs->weaponnum); + } + else { + newweaponnum = trap_BotChooseBestFightWeapon(bs->ws, bs->inventory); + if (bs->weaponnum != newweaponnum) bs->weaponchange_time = FloatTime(); + bs->weaponnum = newweaponnum; + //BotAI_Print(PRT_MESSAGE, "bs->weaponnum = %d\n", bs->weaponnum); + trap_EA_SelectWeapon(bs->client, bs->weaponnum); + } +} + +/* +================== +BotSetupForMovement +================== +*/ +void BotSetupForMovement(bot_state_t *bs) { + bot_initmove_t initmove; + + memset(&initmove, 0, sizeof(bot_initmove_t)); + VectorCopy(bs->cur_ps.origin, initmove.origin); + VectorCopy(bs->cur_ps.velocity, initmove.velocity); + VectorClear(initmove.viewoffset); + initmove.viewoffset[2] += bs->cur_ps.viewheight; + initmove.entitynum = bs->entitynum; + initmove.client = bs->client; + initmove.thinktime = bs->thinktime; + //set the onground flag + if (bs->cur_ps.groundEntityNum != ENTITYNUM_NONE) initmove.or_moveflags |= MFL_ONGROUND; + //set the teleported flag + if ((bs->cur_ps.pm_flags & PMF_TIME_KNOCKBACK) && (bs->cur_ps.pm_time > 0)) { + initmove.or_moveflags |= MFL_TELEPORTED; + } + //set the waterjump flag + if ((bs->cur_ps.pm_flags & PMF_TIME_WATERJUMP) && (bs->cur_ps.pm_time > 0)) { + initmove.or_moveflags |= MFL_WATERJUMP; + } + //set presence type + if (bs->cur_ps.pm_flags & PMF_DUCKED) initmove.presencetype = PRESENCE_CROUCH; + else initmove.presencetype = PRESENCE_NORMAL; + // + if (bs->walker > 0.5) initmove.or_moveflags |= MFL_WALK; + // + VectorCopy(bs->viewangles, initmove.viewangles); + // + trap_BotInitMoveState(bs->ms, &initmove); +} + +/* +================== +BotCheckItemPickup +================== +*/ +void BotCheckItemPickup(bot_state_t *bs, int *oldinventory) { +#ifdef MISSIONPACK + int offence, leader; + + if (gametype <= GT_TEAM) + return; + + offence = -1; + // go into offence if picked up the kamikaze or invulnerability + if (!oldinventory[INVENTORY_KAMIKAZE] && bs->inventory[INVENTORY_KAMIKAZE] >= 1) { + offence = qtrue; + } + if (!oldinventory[INVENTORY_INVULNERABILITY] && bs->inventory[INVENTORY_INVULNERABILITY] >= 1) { + offence = qtrue; + } + // if not already wearing the kamikaze or invulnerability + if (!bs->inventory[INVENTORY_KAMIKAZE] && !bs->inventory[INVENTORY_INVULNERABILITY]) { + if (!oldinventory[INVENTORY_SCOUT] && bs->inventory[INVENTORY_SCOUT] >= 1) { + offence = qtrue; + } + if (!oldinventory[INVENTORY_GUARD] && bs->inventory[INVENTORY_GUARD] >= 1) { + offence = qtrue; + } + if (!oldinventory[INVENTORY_DOUBLER] && bs->inventory[INVENTORY_DOUBLER] >= 1) { + offence = qfalse; + } + if (!oldinventory[INVENTORY_AMMOREGEN] && bs->inventory[INVENTORY_AMMOREGEN] >= 1) { + offence = qfalse; + } + } + + if (offence >= 0) { + leader = ClientFromName(bs->teamleader); + if (offence) { + if (!(bs->teamtaskpreference & TEAMTP_ATTACKER)) { + // if we have a bot team leader + if (BotTeamLeader(bs)) { + // tell the leader we want to be on offence + BotVoiceChat(bs, leader, VOICECHAT_WANTONOFFENSE); + //BotAI_BotInitialChat(bs, "wantoffence", NULL); + //trap_BotEnterChat(bs->cs, leader, CHAT_TELL); + } + else if (g_spSkill.integer <= 3) { + if ( bs->ltgtype != LTG_GETFLAG && + bs->ltgtype != LTG_ATTACKENEMYBASE && + bs->ltgtype != LTG_HARVEST ) { + // + if ((gametype != GT_CTF || (bs->redflagstatus == 0 && bs->blueflagstatus == 0)) && + (gametype != GT_1FCTF || bs->neutralflagstatus == 0) ) { + // tell the leader we want to be on offence + BotVoiceChat(bs, leader, VOICECHAT_WANTONOFFENSE); + //BotAI_BotInitialChat(bs, "wantoffence", NULL); + //trap_BotEnterChat(bs->cs, leader, CHAT_TELL); + } + } + bs->teamtaskpreference |= TEAMTP_ATTACKER; + } + } + bs->teamtaskpreference &= ~TEAMTP_DEFENDER; + } + else { + if (!(bs->teamtaskpreference & TEAMTP_DEFENDER)) { + // if we have a bot team leader + if (BotTeamLeader(bs)) { + // tell the leader we want to be on defense + BotVoiceChat(bs, -1, VOICECHAT_WANTONDEFENSE); + //BotAI_BotInitialChat(bs, "wantdefence", NULL); + //trap_BotEnterChat(bs->cs, leader, CHAT_TELL); + } + else if (g_spSkill.integer <= 3) { + if ( bs->ltgtype != LTG_DEFENDKEYAREA ) { + // + if ((gametype != GT_CTF || (bs->redflagstatus == 0 && bs->blueflagstatus == 0)) && + (gametype != GT_1FCTF || bs->neutralflagstatus == 0) ) { + // tell the leader we want to be on defense + BotVoiceChat(bs, -1, VOICECHAT_WANTONDEFENSE); + //BotAI_BotInitialChat(bs, "wantdefence", NULL); + //trap_BotEnterChat(bs->cs, leader, CHAT_TELL); + } + } + } + bs->teamtaskpreference |= TEAMTP_DEFENDER; + } + bs->teamtaskpreference &= ~TEAMTP_ATTACKER; + } + } +#endif +} + +/* +================== +BotUpdateInventory +================== +*/ +void BotUpdateInventory(bot_state_t *bs) { + int oldinventory[MAX_ITEMS]; + + memcpy(oldinventory, bs->inventory, sizeof(oldinventory)); + //armor + bs->inventory[INVENTORY_ARMOR] = bs->cur_ps.stats[STAT_ARMOR]; + //weapons + bs->inventory[INVENTORY_GAUNTLET] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_GAUNTLET)) != 0; + bs->inventory[INVENTORY_SHOTGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_SHOTGUN)) != 0; + bs->inventory[INVENTORY_MACHINEGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_MACHINEGUN)) != 0; + bs->inventory[INVENTORY_GRENADELAUNCHER] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_GRENADE_LAUNCHER)) != 0; + bs->inventory[INVENTORY_ROCKETLAUNCHER] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_ROCKET_LAUNCHER)) != 0; + bs->inventory[INVENTORY_LIGHTNING] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_LIGHTNING)) != 0; + bs->inventory[INVENTORY_RAILGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_RAILGUN)) != 0; + bs->inventory[INVENTORY_PLASMAGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_PLASMAGUN)) != 0; + bs->inventory[INVENTORY_BFG10K] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_BFG)) != 0; + bs->inventory[INVENTORY_GRAPPLINGHOOK] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_GRAPPLING_HOOK)) != 0; +#ifdef MISSIONPACK + bs->inventory[INVENTORY_NAILGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_NAILGUN)) != 0;; + bs->inventory[INVENTORY_PROXLAUNCHER] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_PROX_LAUNCHER)) != 0;; + bs->inventory[INVENTORY_CHAINGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_CHAINGUN)) != 0;; +#endif + //ammo + bs->inventory[INVENTORY_SHELLS] = bs->cur_ps.ammo[WP_SHOTGUN]; + bs->inventory[INVENTORY_BULLETS] = bs->cur_ps.ammo[WP_MACHINEGUN]; + bs->inventory[INVENTORY_GRENADES] = bs->cur_ps.ammo[WP_GRENADE_LAUNCHER]; + bs->inventory[INVENTORY_CELLS] = bs->cur_ps.ammo[WP_PLASMAGUN]; + bs->inventory[INVENTORY_LIGHTNINGAMMO] = bs->cur_ps.ammo[WP_LIGHTNING]; + bs->inventory[INVENTORY_ROCKETS] = bs->cur_ps.ammo[WP_ROCKET_LAUNCHER]; + bs->inventory[INVENTORY_SLUGS] = bs->cur_ps.ammo[WP_RAILGUN]; + bs->inventory[INVENTORY_BFGAMMO] = bs->cur_ps.ammo[WP_BFG]; +#ifdef MISSIONPACK + bs->inventory[INVENTORY_NAILS] = bs->cur_ps.ammo[WP_NAILGUN]; + bs->inventory[INVENTORY_MINES] = bs->cur_ps.ammo[WP_PROX_LAUNCHER]; + bs->inventory[INVENTORY_BELT] = bs->cur_ps.ammo[WP_CHAINGUN]; +#endif + //powerups + bs->inventory[INVENTORY_HEALTH] = bs->cur_ps.stats[STAT_HEALTH]; + bs->inventory[INVENTORY_TELEPORTER] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_TELEPORTER; + bs->inventory[INVENTORY_MEDKIT] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_MEDKIT; +#ifdef MISSIONPACK + bs->inventory[INVENTORY_KAMIKAZE] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_KAMIKAZE; + bs->inventory[INVENTORY_PORTAL] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_PORTAL; + bs->inventory[INVENTORY_INVULNERABILITY] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_INVULNERABILITY; +#endif + bs->inventory[INVENTORY_QUAD] = bs->cur_ps.powerups[PW_QUAD] != 0; + bs->inventory[INVENTORY_ENVIRONMENTSUIT] = bs->cur_ps.powerups[PW_BATTLESUIT] != 0; + bs->inventory[INVENTORY_HASTE] = bs->cur_ps.powerups[PW_HASTE] != 0; + bs->inventory[INVENTORY_INVISIBILITY] = bs->cur_ps.powerups[PW_INVIS] != 0; + bs->inventory[INVENTORY_REGEN] = bs->cur_ps.powerups[PW_REGEN] != 0; + bs->inventory[INVENTORY_FLIGHT] = bs->cur_ps.powerups[PW_FLIGHT] != 0; +#ifdef MISSIONPACK + bs->inventory[INVENTORY_SCOUT] = bs->cur_ps.stats[STAT_PERSISTANT_POWERUP] == MODELINDEX_SCOUT; + bs->inventory[INVENTORY_GUARD] = bs->cur_ps.stats[STAT_PERSISTANT_POWERUP] == MODELINDEX_GUARD; + bs->inventory[INVENTORY_DOUBLER] = bs->cur_ps.stats[STAT_PERSISTANT_POWERUP] == MODELINDEX_DOUBLER; + bs->inventory[INVENTORY_AMMOREGEN] = bs->cur_ps.stats[STAT_PERSISTANT_POWERUP] == MODELINDEX_AMMOREGEN; +#endif + bs->inventory[INVENTORY_REDFLAG] = bs->cur_ps.powerups[PW_REDFLAG] != 0; + bs->inventory[INVENTORY_BLUEFLAG] = bs->cur_ps.powerups[PW_BLUEFLAG] != 0; +#ifdef MISSIONPACK + bs->inventory[INVENTORY_NEUTRALFLAG] = bs->cur_ps.powerups[PW_NEUTRALFLAG] != 0; + if (BotTeam(bs) == TEAM_RED) { + bs->inventory[INVENTORY_REDCUBE] = bs->cur_ps.generic1; + bs->inventory[INVENTORY_BLUECUBE] = 0; + } + else { + bs->inventory[INVENTORY_REDCUBE] = 0; + bs->inventory[INVENTORY_BLUECUBE] = bs->cur_ps.generic1; + } +#endif + BotCheckItemPickup(bs, oldinventory); +} + +/* +================== +BotUpdateBattleInventory +================== +*/ +void BotUpdateBattleInventory(bot_state_t *bs, int enemy) { + vec3_t dir; + aas_entityinfo_t entinfo; + + BotEntityInfo(enemy, &entinfo); + VectorSubtract(entinfo.origin, bs->origin, dir); + bs->inventory[ENEMY_HEIGHT] = (int) dir[2]; + dir[2] = 0; + bs->inventory[ENEMY_HORIZONTAL_DIST] = (int) VectorLength(dir); + //FIXME: add num visible enemies and num visible team mates to the inventory +} + +#ifdef MISSIONPACK +/* +================== +BotUseKamikaze +================== +*/ +#define KAMIKAZE_DIST 1024 + +void BotUseKamikaze(bot_state_t *bs) { + int c, teammates, enemies; + aas_entityinfo_t entinfo; + vec3_t dir, target; + bot_goal_t *goal; + bsp_trace_t trace; + + //if the bot has no kamikaze + if (bs->inventory[INVENTORY_KAMIKAZE] <= 0) + return; + if (bs->kamikaze_time > FloatTime()) + return; + bs->kamikaze_time = FloatTime() + 0.2; + if (gametype == GT_CTF) { + //never use kamikaze if the team flag carrier is visible + if (BotCTFCarryingFlag(bs)) + return; + c = BotTeamFlagCarrierVisible(bs); + if (c >= 0) { + BotEntityInfo(c, &entinfo); + VectorSubtract(entinfo.origin, bs->origin, dir); + if (VectorLengthSquared(dir) < Square(KAMIKAZE_DIST)) + return; + } + c = BotEnemyFlagCarrierVisible(bs); + if (c >= 0) { + BotEntityInfo(c, &entinfo); + VectorSubtract(entinfo.origin, bs->origin, dir); + if (VectorLengthSquared(dir) < Square(KAMIKAZE_DIST)) { + trap_EA_Use(bs->client); + return; + } + } + } + else if (gametype == GT_1FCTF) { + //never use kamikaze if the team flag carrier is visible + if (Bot1FCTFCarryingFlag(bs)) + return; + c = BotTeamFlagCarrierVisible(bs); + if (c >= 0) { + BotEntityInfo(c, &entinfo); + VectorSubtract(entinfo.origin, bs->origin, dir); + if (VectorLengthSquared(dir) < Square(KAMIKAZE_DIST)) + return; + } + c = BotEnemyFlagCarrierVisible(bs); + if (c >= 0) { + BotEntityInfo(c, &entinfo); + VectorSubtract(entinfo.origin, bs->origin, dir); + if (VectorLengthSquared(dir) < Square(KAMIKAZE_DIST)) { + trap_EA_Use(bs->client); + return; + } + } + } + else if (gametype == GT_OBELISK) { + switch(BotTeam(bs)) { + case TEAM_RED: goal = &blueobelisk; break; + default: goal = &redobelisk; break; + } + //if the obelisk is visible + VectorCopy(goal->origin, target); + target[2] += 1; + VectorSubtract(bs->origin, target, dir); + if (VectorLengthSquared(dir) < Square(KAMIKAZE_DIST * 0.9)) { + BotAI_Trace(&trace, bs->eye, NULL, NULL, target, bs->client, CONTENTS_SOLID); + if (trace.fraction >= 1 || trace.ent == goal->entitynum) { + trap_EA_Use(bs->client); + return; + } + } + } + else if (gametype == GT_HARVESTER) { + // + if (BotHarvesterCarryingCubes(bs)) + return; + //never use kamikaze if a team mate carrying cubes is visible + c = BotTeamCubeCarrierVisible(bs); + if (c >= 0) { + BotEntityInfo(c, &entinfo); + VectorSubtract(entinfo.origin, bs->origin, dir); + if (VectorLengthSquared(dir) < Square(KAMIKAZE_DIST)) + return; + } + c = BotEnemyCubeCarrierVisible(bs); + if (c >= 0) { + BotEntityInfo(c, &entinfo); + VectorSubtract(entinfo.origin, bs->origin, dir); + if (VectorLengthSquared(dir) < Square(KAMIKAZE_DIST)) { + trap_EA_Use(bs->client); + return; + } + } + } + // + BotVisibleTeamMatesAndEnemies(bs, &teammates, &enemies, KAMIKAZE_DIST); + // + if (enemies > 2 && enemies > teammates+1) { + trap_EA_Use(bs->client); + return; + } +} + +/* +================== +BotUseInvulnerability +================== +*/ +void BotUseInvulnerability(bot_state_t *bs) { + int c; + vec3_t dir, target; + bot_goal_t *goal; + bsp_trace_t trace; + + //if the bot has no invulnerability + if (bs->inventory[INVENTORY_INVULNERABILITY] <= 0) + return; + if (bs->invulnerability_time > FloatTime()) + return; + bs->invulnerability_time = FloatTime() + 0.2; + if (gametype == GT_CTF) { + //never use kamikaze if the team flag carrier is visible + if (BotCTFCarryingFlag(bs)) + return; + c = BotEnemyFlagCarrierVisible(bs); + if (c >= 0) + return; + //if near enemy flag and the flag is visible + switch(BotTeam(bs)) { + case TEAM_RED: goal = &ctf_blueflag; break; + default: goal = &ctf_redflag; break; + } + //if the obelisk is visible + VectorCopy(goal->origin, target); + target[2] += 1; + VectorSubtract(bs->origin, target, dir); + if (VectorLengthSquared(dir) < Square(200)) { + BotAI_Trace(&trace, bs->eye, NULL, NULL, target, bs->client, CONTENTS_SOLID); + if (trace.fraction >= 1 || trace.ent == goal->entitynum) { + trap_EA_Use(bs->client); + return; + } + } + } + else if (gametype == GT_1FCTF) { + //never use kamikaze if the team flag carrier is visible + if (Bot1FCTFCarryingFlag(bs)) + return; + c = BotEnemyFlagCarrierVisible(bs); + if (c >= 0) + return; + //if near enemy flag and the flag is visible + switch(BotTeam(bs)) { + case TEAM_RED: goal = &ctf_blueflag; break; + default: goal = &ctf_redflag; break; + } + //if the obelisk is visible + VectorCopy(goal->origin, target); + target[2] += 1; + VectorSubtract(bs->origin, target, dir); + if (VectorLengthSquared(dir) < Square(200)) { + BotAI_Trace(&trace, bs->eye, NULL, NULL, target, bs->client, CONTENTS_SOLID); + if (trace.fraction >= 1 || trace.ent == goal->entitynum) { + trap_EA_Use(bs->client); + return; + } + } + } + else if (gametype == GT_OBELISK) { + switch(BotTeam(bs)) { + case TEAM_RED: goal = &blueobelisk; break; + default: goal = &redobelisk; break; + } + //if the obelisk is visible + VectorCopy(goal->origin, target); + target[2] += 1; + VectorSubtract(bs->origin, target, dir); + if (VectorLengthSquared(dir) < Square(300)) { + BotAI_Trace(&trace, bs->eye, NULL, NULL, target, bs->client, CONTENTS_SOLID); + if (trace.fraction >= 1 || trace.ent == goal->entitynum) { + trap_EA_Use(bs->client); + return; + } + } + } + else if (gametype == GT_HARVESTER) { + // + if (BotHarvesterCarryingCubes(bs)) + return; + c = BotEnemyCubeCarrierVisible(bs); + if (c >= 0) + return; + //if near enemy base and enemy base is visible + switch(BotTeam(bs)) { + case TEAM_RED: goal = &blueobelisk; break; + default: goal = &redobelisk; break; + } + //if the obelisk is visible + VectorCopy(goal->origin, target); + target[2] += 1; + VectorSubtract(bs->origin, target, dir); + if (VectorLengthSquared(dir) < Square(200)) { + BotAI_Trace(&trace, bs->eye, NULL, NULL, target, bs->client, CONTENTS_SOLID); + if (trace.fraction >= 1 || trace.ent == goal->entitynum) { + trap_EA_Use(bs->client); + return; + } + } + } +} +#endif + +/* +================== +BotBattleUseItems +================== +*/ +void BotBattleUseItems(bot_state_t *bs) { + if (bs->inventory[INVENTORY_HEALTH] < 40) { + if (bs->inventory[INVENTORY_TELEPORTER] > 0) { + if (!BotCTFCarryingFlag(bs) +#ifdef MISSIONPACK + && !Bot1FCTFCarryingFlag(bs) + && !BotHarvesterCarryingCubes(bs) +#endif + ) { + trap_EA_Use(bs->client); + } + } + } + if (bs->inventory[INVENTORY_HEALTH] < 60) { + if (bs->inventory[INVENTORY_MEDKIT] > 0) { + trap_EA_Use(bs->client); + } + } +#ifdef MISSIONPACK + BotUseKamikaze(bs); + BotUseInvulnerability(bs); +#endif +} + +/* +================== +BotSetTeleportTime +================== +*/ +void BotSetTeleportTime(bot_state_t *bs) { + if ((bs->cur_ps.eFlags ^ bs->last_eFlags) & EF_TELEPORT_BIT) { + bs->teleport_time = FloatTime(); + } + bs->last_eFlags = bs->cur_ps.eFlags; +} + +/* +================== +BotIsDead +================== +*/ +qboolean BotIsDead(bot_state_t *bs) { + return (bs->cur_ps.pm_type == PM_DEAD); +} + +/* +================== +BotIsObserver +================== +*/ +qboolean BotIsObserver(bot_state_t *bs) { + char buf[MAX_INFO_STRING]; + if (bs->cur_ps.pm_type == PM_SPECTATOR) return qtrue; + trap_GetConfigstring(CS_PLAYERS+bs->client, buf, sizeof(buf)); + if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) return qtrue; + return qfalse; +} + +/* +================== +BotIntermission +================== +*/ +qboolean BotIntermission(bot_state_t *bs) { + //NOTE: we shouldn't be looking at the game code... + if (level.intermissiontime) return qtrue; + return (bs->cur_ps.pm_type == PM_FREEZE || bs->cur_ps.pm_type == PM_INTERMISSION); +} + +/* +================== +BotInLavaOrSlime +================== +*/ +qboolean BotInLavaOrSlime(bot_state_t *bs) { + vec3_t feet; + + VectorCopy(bs->origin, feet); + feet[2] -= 23; + return (trap_AAS_PointContents(feet) & (CONTENTS_LAVA|CONTENTS_SLIME)); +} + +/* +================== +BotCreateWayPoint +================== +*/ +bot_waypoint_t *BotCreateWayPoint(char *name, vec3_t origin, int areanum) { + bot_waypoint_t *wp; + vec3_t waypointmins = {-8, -8, -8}, waypointmaxs = {8, 8, 8}; + + wp = botai_freewaypoints; + if ( !wp ) { + BotAI_Print( PRT_WARNING, "BotCreateWayPoint: Out of waypoints\n" ); + return NULL; + } + botai_freewaypoints = botai_freewaypoints->next; + + Q_strncpyz( wp->name, name, sizeof(wp->name) ); + VectorCopy(origin, wp->goal.origin); + VectorCopy(waypointmins, wp->goal.mins); + VectorCopy(waypointmaxs, wp->goal.maxs); + wp->goal.areanum = areanum; + wp->next = NULL; + wp->prev = NULL; + return wp; +} + +/* +================== +BotFindWayPoint +================== +*/ +bot_waypoint_t *BotFindWayPoint(bot_waypoint_t *waypoints, char *name) { + bot_waypoint_t *wp; + + for (wp = waypoints; wp; wp = wp->next) { + if (!Q_stricmp(wp->name, name)) return wp; + } + return NULL; +} + +/* +================== +BotFreeWaypoints +================== +*/ +void BotFreeWaypoints(bot_waypoint_t *wp) { + bot_waypoint_t *nextwp; + + for (; wp; wp = nextwp) { + nextwp = wp->next; + wp->next = botai_freewaypoints; + botai_freewaypoints = wp; + } +} + +/* +================== +BotInitWaypoints +================== +*/ +void BotInitWaypoints(void) { + int i; + + botai_freewaypoints = NULL; + for (i = 0; i < MAX_WAYPOINTS; i++) { + botai_waypoints[i].next = botai_freewaypoints; + botai_freewaypoints = &botai_waypoints[i]; + } +} + +/* +================== +TeamPlayIsOn +================== +*/ +int TeamPlayIsOn(void) { + return ( gametype >= GT_TEAM ); +} + +/* +================== +BotAggression +================== +*/ +float BotAggression(bot_state_t *bs) { + //if the bot has quad + if (bs->inventory[INVENTORY_QUAD]) { + //if the bot is not holding the gauntlet or the enemy is really nearby + if (bs->weaponnum != WP_GAUNTLET || + bs->inventory[ENEMY_HORIZONTAL_DIST] < 80) { + return 70; + } + } + //if the enemy is located way higher than the bot + if (bs->inventory[ENEMY_HEIGHT] > 200) return 0; + //if the bot is very low on health + if (bs->inventory[INVENTORY_HEALTH] < 60) return 0; + //if the bot is low on health + if (bs->inventory[INVENTORY_HEALTH] < 80) { + //if the bot has insufficient armor + if (bs->inventory[INVENTORY_ARMOR] < 40) return 0; + } + //if the bot can use the bfg + if (bs->inventory[INVENTORY_BFG10K] > 0 && + bs->inventory[INVENTORY_BFGAMMO] > 7) return 100; + //if the bot can use the railgun + if (bs->inventory[INVENTORY_RAILGUN] > 0 && + bs->inventory[INVENTORY_SLUGS] > 5) return 95; + //if the bot can use the lightning gun + if (bs->inventory[INVENTORY_LIGHTNING] > 0 && + bs->inventory[INVENTORY_LIGHTNINGAMMO] > 50) return 90; + //if the bot can use the rocketlauncher + if (bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 && + bs->inventory[INVENTORY_ROCKETS] > 5) return 90; + //if the bot can use the plasmagun + if (bs->inventory[INVENTORY_PLASMAGUN] > 0 && + bs->inventory[INVENTORY_CELLS] > 40) return 85; + //if the bot can use the grenade launcher + if (bs->inventory[INVENTORY_GRENADELAUNCHER] > 0 && + bs->inventory[INVENTORY_GRENADES] > 10) return 80; + //if the bot can use the shotgun + if (bs->inventory[INVENTORY_SHOTGUN] > 0 && + bs->inventory[INVENTORY_SHELLS] > 10) return 50; + //otherwise the bot is not feeling too good + return 0; +} + +/* +================== +BotFeelingBad +================== +*/ +float BotFeelingBad(bot_state_t *bs) { + if (bs->weaponnum == WP_GAUNTLET) { + return 100; + } + if (bs->inventory[INVENTORY_HEALTH] < 40) { + return 100; + } + if (bs->weaponnum == WP_MACHINEGUN) { + return 90; + } + if (bs->inventory[INVENTORY_HEALTH] < 60) { + return 80; + } + return 0; +} + +/* +================== +BotWantsToRetreat +================== +*/ +int BotWantsToRetreat(bot_state_t *bs) { + aas_entityinfo_t entinfo; + + if (gametype == GT_CTF) { + //always retreat when carrying a CTF flag + if (BotCTFCarryingFlag(bs)) + return qtrue; + } +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF) { + //if carrying the flag then always retreat + if (Bot1FCTFCarryingFlag(bs)) + return qtrue; + } + else if (gametype == GT_OBELISK) { + //the bots should be dedicated to attacking the enemy obelisk + if (bs->ltgtype == LTG_ATTACKENEMYBASE) { + if (bs->enemy != redobelisk.entitynum || + bs->enemy != blueobelisk.entitynum) { + return qtrue; + } + } + if (BotFeelingBad(bs) > 50) { + return qtrue; + } + return qfalse; + } + else if (gametype == GT_HARVESTER) { + //if carrying cubes then always retreat + if (BotHarvesterCarryingCubes(bs)) return qtrue; + } +#endif + // + if (bs->enemy >= 0) { + //if the enemy is carrying a flag + BotEntityInfo(bs->enemy, &entinfo); + if (EntityCarriesFlag(&entinfo)) + return qfalse; + } + //if the bot is getting the flag + if (bs->ltgtype == LTG_GETFLAG) + return qtrue; + // + if (BotAggression(bs) < 50) + return qtrue; + return qfalse; +} + +/* +================== +BotWantsToChase +================== +*/ +int BotWantsToChase(bot_state_t *bs) { + aas_entityinfo_t entinfo; + + if (gametype == GT_CTF) { + //never chase when carrying a CTF flag + if (BotCTFCarryingFlag(bs)) + return qfalse; + //always chase if the enemy is carrying a flag + BotEntityInfo(bs->enemy, &entinfo); + if (EntityCarriesFlag(&entinfo)) + return qtrue; + } +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF) { + //never chase if carrying the flag + if (Bot1FCTFCarryingFlag(bs)) + return qfalse; + //always chase if the enemy is carrying a flag + BotEntityInfo(bs->enemy, &entinfo); + if (EntityCarriesFlag(&entinfo)) + return qtrue; + } + else if (gametype == GT_OBELISK) { + //the bots should be dedicated to attacking the enemy obelisk + if (bs->ltgtype == LTG_ATTACKENEMYBASE) { + if (bs->enemy != redobelisk.entitynum || + bs->enemy != blueobelisk.entitynum) { + return qfalse; + } + } + } + else if (gametype == GT_HARVESTER) { + //never chase if carrying cubes + if (BotHarvesterCarryingCubes(bs)) + return qfalse; + } +#endif + //if the bot is getting the flag + if (bs->ltgtype == LTG_GETFLAG) + return qfalse; + // + if (BotAggression(bs) > 50) + return qtrue; + return qfalse; +} + +/* +================== +BotWantsToHelp +================== +*/ +int BotWantsToHelp(bot_state_t *bs) { + return qtrue; +} + +/* +================== +BotCanAndWantsToRocketJump +================== +*/ +int BotCanAndWantsToRocketJump(bot_state_t *bs) { + float rocketjumper; + + //if rocket jumping is disabled + if (!bot_rocketjump.integer) return qfalse; + //if no rocket launcher + if (bs->inventory[INVENTORY_ROCKETLAUNCHER] <= 0) return qfalse; + //if low on rockets + if (bs->inventory[INVENTORY_ROCKETS] < 3) return qfalse; + //never rocket jump with the Quad + if (bs->inventory[INVENTORY_QUAD]) return qfalse; + //if low on health + if (bs->inventory[INVENTORY_HEALTH] < 60) return qfalse; + //if not full health + if (bs->inventory[INVENTORY_HEALTH] < 90) { + //if the bot has insufficient armor + if (bs->inventory[INVENTORY_ARMOR] < 40) return qfalse; + } + rocketjumper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_WEAPONJUMPING, 0, 1); + if (rocketjumper < 0.5) return qfalse; + return qtrue; +} + +/* +================== +BotHasPersistantPowerupAndWeapon +================== +*/ +int BotHasPersistantPowerupAndWeapon(bot_state_t *bs) { +#ifdef MISSIONPACK + // if the bot does not have a persistant powerup + if (!bs->inventory[INVENTORY_SCOUT] && + !bs->inventory[INVENTORY_GUARD] && + !bs->inventory[INVENTORY_DOUBLER] && + !bs->inventory[INVENTORY_AMMOREGEN] ) { + return qfalse; + } +#endif + //if the bot is very low on health + if (bs->inventory[INVENTORY_HEALTH] < 60) return qfalse; + //if the bot is low on health + if (bs->inventory[INVENTORY_HEALTH] < 80) { + //if the bot has insufficient armor + if (bs->inventory[INVENTORY_ARMOR] < 40) return qfalse; + } + //if the bot can use the bfg + if (bs->inventory[INVENTORY_BFG10K] > 0 && + bs->inventory[INVENTORY_BFGAMMO] > 7) return qtrue; + //if the bot can use the railgun + if (bs->inventory[INVENTORY_RAILGUN] > 0 && + bs->inventory[INVENTORY_SLUGS] > 5) return qtrue; + //if the bot can use the lightning gun + if (bs->inventory[INVENTORY_LIGHTNING] > 0 && + bs->inventory[INVENTORY_LIGHTNINGAMMO] > 50) return qtrue; + //if the bot can use the rocketlauncher + if (bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 && + bs->inventory[INVENTORY_ROCKETS] > 5) return qtrue; + // + if (bs->inventory[INVENTORY_NAILGUN] > 0 && + bs->inventory[INVENTORY_NAILS] > 5) return qtrue; + // + if (bs->inventory[INVENTORY_PROXLAUNCHER] > 0 && + bs->inventory[INVENTORY_MINES] > 5) return qtrue; + // + if (bs->inventory[INVENTORY_CHAINGUN] > 0 && + bs->inventory[INVENTORY_BELT] > 40) return qtrue; + //if the bot can use the plasmagun + if (bs->inventory[INVENTORY_PLASMAGUN] > 0 && + bs->inventory[INVENTORY_CELLS] > 20) return qtrue; + return qfalse; +} + +/* +================== +BotGoCamp +================== +*/ +void BotGoCamp(bot_state_t *bs, bot_goal_t *goal) { + float camper; + + bs->decisionmaker = bs->client; + //set message time to zero so bot will NOT show any message + bs->teammessage_time = 0; + //set the ltg type + bs->ltgtype = LTG_CAMP; + //set the team goal + memcpy(&bs->teamgoal, goal, sizeof(bot_goal_t)); + //get the team goal time + camper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CAMPER, 0, 1); + if (camper > 0.99) bs->teamgoal_time = FloatTime() + 99999; + else bs->teamgoal_time = FloatTime() + 120 + 180 * camper + random() * 15; + //set the last time the bot started camping + bs->camp_time = FloatTime(); + //the teammate that requested the camping + bs->teammate = 0; + //do NOT type arrive message + bs->arrive_time = 1; +} + +/* +================== +BotWantsToCamp +================== +*/ +int BotWantsToCamp(bot_state_t *bs) { + float camper; + int cs, traveltime, besttraveltime; + bot_goal_t goal, bestgoal; + + camper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CAMPER, 0, 1); + if (camper < 0.1) return qfalse; + //if the bot has a team goal + if (bs->ltgtype == LTG_TEAMHELP || + bs->ltgtype == LTG_TEAMACCOMPANY || + bs->ltgtype == LTG_DEFENDKEYAREA || + bs->ltgtype == LTG_GETFLAG || + bs->ltgtype == LTG_RUSHBASE || + bs->ltgtype == LTG_CAMP || + bs->ltgtype == LTG_CAMPORDER || + bs->ltgtype == LTG_PATROL) { + return qfalse; + } + //if camped recently + if (bs->camp_time > FloatTime() - 60 + 300 * (1-camper)) return qfalse; + // + if (random() > camper) { + bs->camp_time = FloatTime(); + return qfalse; + } + //if the bot isn't healthy anough + if (BotAggression(bs) < 50) return qfalse; + //the bot should have at least have the rocket launcher, the railgun or the bfg10k with some ammo + if ((bs->inventory[INVENTORY_ROCKETLAUNCHER] <= 0 || bs->inventory[INVENTORY_ROCKETS < 10]) && + (bs->inventory[INVENTORY_RAILGUN] <= 0 || bs->inventory[INVENTORY_SLUGS] < 10) && + (bs->inventory[INVENTORY_BFG10K] <= 0 || bs->inventory[INVENTORY_BFGAMMO] < 10)) { + return qfalse; + } + //find the closest camp spot + besttraveltime = 99999; + for (cs = trap_BotGetNextCampSpotGoal(0, &goal); cs; cs = trap_BotGetNextCampSpotGoal(cs, &goal)) { + traveltime = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, goal.areanum, TFL_DEFAULT); + if (traveltime && traveltime < besttraveltime) { + besttraveltime = traveltime; + memcpy(&bestgoal, &goal, sizeof(bot_goal_t)); + } + } + if (besttraveltime > 150) return qfalse; + //ok found a camp spot, go camp there + BotGoCamp(bs, &bestgoal); + bs->ordered = qfalse; + // + return qtrue; +} + +/* +================== +BotDontAvoid +================== +*/ +void BotDontAvoid(bot_state_t *bs, char *itemname) { + bot_goal_t goal; + int num; + + num = trap_BotGetLevelItemGoal(-1, itemname, &goal); + while(num >= 0) { + trap_BotRemoveFromAvoidGoals(bs->gs, goal.number); + num = trap_BotGetLevelItemGoal(num, itemname, &goal); + } +} + +/* +================== +BotGoForPowerups +================== +*/ +void BotGoForPowerups(bot_state_t *bs) { + + //don't avoid any of the powerups anymore + BotDontAvoid(bs, "Quad Damage"); + BotDontAvoid(bs, "Regeneration"); + BotDontAvoid(bs, "Battle Suit"); + BotDontAvoid(bs, "Speed"); + BotDontAvoid(bs, "Invisibility"); + //BotDontAvoid(bs, "Flight"); + //reset the long term goal time so the bot will go for the powerup + //NOTE: the long term goal type doesn't change + bs->ltg_time = 0; +} + +/* +================== +BotRoamGoal +================== +*/ +void BotRoamGoal(bot_state_t *bs, vec3_t goal) { + int pc, i; + float len, rnd; + vec3_t dir, bestorg, belowbestorg; + bsp_trace_t trace; + + for (i = 0; i < 10; i++) { + //start at the bot origin + VectorCopy(bs->origin, bestorg); + rnd = random(); + if (rnd > 0.25) { + //add a random value to the x-coordinate + if (random() < 0.5) bestorg[0] -= 800 * random() + 100; + else bestorg[0] += 800 * random() + 100; + } + if (rnd < 0.75) { + //add a random value to the y-coordinate + if (random() < 0.5) bestorg[1] -= 800 * random() + 100; + else bestorg[1] += 800 * random() + 100; + } + //add a random value to the z-coordinate (NOTE: 48 = maxjump?) + bestorg[2] += 2 * 48 * crandom(); + //trace a line from the origin to the roam target + BotAI_Trace(&trace, bs->origin, NULL, NULL, bestorg, bs->entitynum, MASK_SOLID); + //direction and length towards the roam target + VectorSubtract(trace.endpos, bs->origin, dir); + len = VectorNormalize(dir); + //if the roam target is far away anough + if (len > 200) { + //the roam target is in the given direction before walls + VectorScale(dir, len * trace.fraction - 40, dir); + VectorAdd(bs->origin, dir, bestorg); + //get the coordinates of the floor below the roam target + belowbestorg[0] = bestorg[0]; + belowbestorg[1] = bestorg[1]; + belowbestorg[2] = bestorg[2] - 800; + BotAI_Trace(&trace, bestorg, NULL, NULL, belowbestorg, bs->entitynum, MASK_SOLID); + // + if (!trace.startsolid) { + trace.endpos[2]++; + pc = trap_PointContents(trace.endpos, bs->entitynum); + if (!(pc & (CONTENTS_LAVA | CONTENTS_SLIME))) { + VectorCopy(bestorg, goal); + return; + } + } + } + } + VectorCopy(bestorg, goal); +} + +/* +================== +BotAttackMove +================== +*/ +bot_moveresult_t BotAttackMove(bot_state_t *bs, int tfl) { + int movetype, i, attackentity; + float attack_skill, jumper, croucher, dist, strafechange_time; + float attack_dist, attack_range; + vec3_t forward, backward, sideward, hordir, up = {0, 0, 1}; + aas_entityinfo_t entinfo; + bot_moveresult_t moveresult; + bot_goal_t goal; + + attackentity = bs->enemy; + // + if (bs->attackchase_time > FloatTime()) { + //create the chase goal + goal.entitynum = attackentity; + goal.areanum = bs->lastenemyareanum; + VectorCopy(bs->lastenemyorigin, goal.origin); + VectorSet(goal.mins, -8, -8, -8); + VectorSet(goal.maxs, 8, 8, 8); + //initialize the movement state + BotSetupForMovement(bs); + //move towards the goal + trap_BotMoveToGoal(&moveresult, bs->ms, &goal, tfl); + return moveresult; + } + // + memset(&moveresult, 0, sizeof(bot_moveresult_t)); + // + attack_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ATTACK_SKILL, 0, 1); + jumper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_JUMPER, 0, 1); + croucher = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CROUCHER, 0, 1); + //if the bot is really stupid + if (attack_skill < 0.2) return moveresult; + //initialize the movement state + BotSetupForMovement(bs); + //get the enemy entity info + BotEntityInfo(attackentity, &entinfo); + //direction towards the enemy + VectorSubtract(entinfo.origin, bs->origin, forward); + //the distance towards the enemy + dist = VectorNormalize(forward); + VectorNegate(forward, backward); + //walk, crouch or jump + movetype = MOVE_WALK; + // + if (bs->attackcrouch_time < FloatTime() - 1) { + if (random() < jumper) { + movetype = MOVE_JUMP; + } + //wait at least one second before crouching again + else if (bs->attackcrouch_time < FloatTime() - 1 && random() < croucher) { + bs->attackcrouch_time = FloatTime() + croucher * 5; + } + } + if (bs->attackcrouch_time > FloatTime()) movetype = MOVE_CROUCH; + //if the bot should jump + if (movetype == MOVE_JUMP) { + //if jumped last frame + if (bs->attackjump_time > FloatTime()) { + movetype = MOVE_WALK; + } + else { + bs->attackjump_time = FloatTime() + 1; + } + } + if (bs->cur_ps.weapon == WP_GAUNTLET) { + attack_dist = 0; + attack_range = 0; + } + else { + attack_dist = IDEAL_ATTACKDIST; + attack_range = 40; + } + //if the bot is stupid + if (attack_skill <= 0.4) { + //just walk to or away from the enemy + if (dist > attack_dist + attack_range) { + if (trap_BotMoveInDirection(bs->ms, forward, 400, movetype)) return moveresult; + } + if (dist < attack_dist - attack_range) { + if (trap_BotMoveInDirection(bs->ms, backward, 400, movetype)) return moveresult; + } + return moveresult; + } + //increase the strafe time + bs->attackstrafe_time += bs->thinktime; + //get the strafe change time + strafechange_time = 0.4 + (1 - attack_skill) * 0.2; + if (attack_skill > 0.7) strafechange_time += crandom() * 0.2; + //if the strafe direction should be changed + if (bs->attackstrafe_time > strafechange_time) { + //some magic number :) + if (random() > 0.935) { + //flip the strafe direction + bs->flags ^= BFL_STRAFERIGHT; + bs->attackstrafe_time = 0; + } + } + // + for (i = 0; i < 2; i++) { + hordir[0] = forward[0]; + hordir[1] = forward[1]; + hordir[2] = 0; + VectorNormalize(hordir); + //get the sideward vector + CrossProduct(hordir, up, sideward); + //reverse the vector depending on the strafe direction + if (bs->flags & BFL_STRAFERIGHT) VectorNegate(sideward, sideward); + //randomly go back a little + if (random() > 0.9) { + VectorAdd(sideward, backward, sideward); + } + else { + //walk forward or backward to get at the ideal attack distance + if (dist > attack_dist + attack_range) { + VectorAdd(sideward, forward, sideward); + } + else if (dist < attack_dist - attack_range) { + VectorAdd(sideward, backward, sideward); + } + } + //perform the movement + if (trap_BotMoveInDirection(bs->ms, sideward, 400, movetype)) + return moveresult; + //movement failed, flip the strafe direction + bs->flags ^= BFL_STRAFERIGHT; + bs->attackstrafe_time = 0; + } + //bot couldn't do any usefull movement +// bs->attackchase_time = AAS_Time() + 6; + return moveresult; +} + +/* +================== +BotSameTeam +================== +*/ +int BotSameTeam(bot_state_t *bs, int entnum) { + char info1[1024], info2[1024]; + + if (bs->client < 0 || bs->client >= MAX_CLIENTS) { + //BotAI_Print(PRT_ERROR, "BotSameTeam: client out of range\n"); + return qfalse; + } + if (entnum < 0 || entnum >= MAX_CLIENTS) { + //BotAI_Print(PRT_ERROR, "BotSameTeam: client out of range\n"); + return qfalse; + } + if ( gametype >= GT_TEAM ) { + trap_GetConfigstring(CS_PLAYERS+bs->client, info1, sizeof(info1)); + trap_GetConfigstring(CS_PLAYERS+entnum, info2, sizeof(info2)); + // + if (atoi(Info_ValueForKey(info1, "t")) == atoi(Info_ValueForKey(info2, "t"))) return qtrue; + } + return qfalse; +} + +/* +================== +InFieldOfVision +================== +*/ +qboolean InFieldOfVision(vec3_t viewangles, float fov, vec3_t angles) +{ + int i; + float diff, angle; + + for (i = 0; i < 2; i++) { + angle = AngleMod(viewangles[i]); + angles[i] = AngleMod(angles[i]); + diff = angles[i] - angle; + if (angles[i] > angle) { + if (diff > 180.0) diff -= 360.0; + } + else { + if (diff < -180.0) diff += 360.0; + } + if (diff > 0) { + if (diff > fov * 0.5) return qfalse; + } + else { + if (diff < -fov * 0.5) return qfalse; + } + } + return qtrue; +} + +/* +================== +BotEntityVisible + +returns visibility in the range [0, 1] taking fog and water surfaces into account +================== +*/ +float BotEntityVisible(int viewer, vec3_t eye, vec3_t viewangles, float fov, int ent) { + int i, contents_mask, passent, hitent, infog, inwater, otherinfog, pc; + float squaredfogdist, waterfactor, vis, bestvis; + bsp_trace_t trace; + aas_entityinfo_t entinfo; + vec3_t dir, entangles, start, end, middle; + + //calculate middle of bounding box + BotEntityInfo(ent, &entinfo); + VectorAdd(entinfo.mins, entinfo.maxs, middle); + VectorScale(middle, 0.5, middle); + VectorAdd(entinfo.origin, middle, middle); + //check if entity is within field of vision + VectorSubtract(middle, eye, dir); + vectoangles(dir, entangles); + if (!InFieldOfVision(viewangles, fov, entangles)) return 0; + // + pc = trap_AAS_PointContents(eye); + infog = (pc & CONTENTS_FOG); + inwater = (pc & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)); + // + bestvis = 0; + for (i = 0; i < 3; i++) { + //if the point is not in potential visible sight + //if (!AAS_inPVS(eye, middle)) continue; + // + contents_mask = CONTENTS_SOLID|CONTENTS_PLAYERCLIP; + passent = viewer; + hitent = ent; + VectorCopy(eye, start); + VectorCopy(middle, end); + //if the entity is in water, lava or slime + if (trap_AAS_PointContents(middle) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) { + contents_mask |= (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER); + } + //if eye is in water, lava or slime + if (inwater) { + if (!(contents_mask & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) { + passent = ent; + hitent = viewer; + VectorCopy(middle, start); + VectorCopy(eye, end); + } + contents_mask ^= (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER); + } + //trace from start to end + BotAI_Trace(&trace, start, NULL, NULL, end, passent, contents_mask); + //if water was hit + waterfactor = 1.0; + if (trace.contents & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) { + //if the water surface is translucent + if (1) { + //trace through the water + contents_mask &= ~(CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER); + BotAI_Trace(&trace, trace.endpos, NULL, NULL, end, passent, contents_mask); + waterfactor = 0.5; + } + } + //if a full trace or the hitent was hit + if (trace.fraction >= 1 || trace.ent == hitent) { + //check for fog, assuming there's only one fog brush where + //either the viewer or the entity is in or both are in + otherinfog = (trap_AAS_PointContents(middle) & CONTENTS_FOG); + if (infog && otherinfog) { + VectorSubtract(trace.endpos, eye, dir); + squaredfogdist = VectorLengthSquared(dir); + } + else if (infog) { + VectorCopy(trace.endpos, start); + BotAI_Trace(&trace, start, NULL, NULL, eye, viewer, CONTENTS_FOG); + VectorSubtract(eye, trace.endpos, dir); + squaredfogdist = VectorLengthSquared(dir); + } + else if (otherinfog) { + VectorCopy(trace.endpos, end); + BotAI_Trace(&trace, eye, NULL, NULL, end, viewer, CONTENTS_FOG); + VectorSubtract(end, trace.endpos, dir); + squaredfogdist = VectorLengthSquared(dir); + } + else { + //if the entity and the viewer are not in fog assume there's no fog in between + squaredfogdist = 0; + } + //decrease visibility with the view distance through fog + vis = 1 / ((squaredfogdist * 0.001) < 1 ? 1 : (squaredfogdist * 0.001)); + //if entering water visibility is reduced + vis *= waterfactor; + // + if (vis > bestvis) bestvis = vis; + //if pretty much no fog + if (bestvis >= 0.95) return bestvis; + } + //check bottom and top of bounding box as well + if (i == 0) middle[2] += entinfo.mins[2]; + else if (i == 1) middle[2] += entinfo.maxs[2] - entinfo.mins[2]; + } + return bestvis; +} + +/* +================== +BotFindEnemy +================== +*/ +int BotFindEnemy(bot_state_t *bs, int curenemy) { + int i, healthdecrease; + float f, alertness, easyfragger, vis; + float squaredist, cursquaredist; + aas_entityinfo_t entinfo, curenemyinfo; + vec3_t dir, angles; + + alertness = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ALERTNESS, 0, 1); + easyfragger = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_EASY_FRAGGER, 0, 1); + //check if the health decreased + healthdecrease = bs->lasthealth > bs->inventory[INVENTORY_HEALTH]; + //remember the current health value + bs->lasthealth = bs->inventory[INVENTORY_HEALTH]; + // + if (curenemy >= 0) { + BotEntityInfo(curenemy, &curenemyinfo); + if (EntityCarriesFlag(&curenemyinfo)) return qfalse; + VectorSubtract(curenemyinfo.origin, bs->origin, dir); + cursquaredist = VectorLengthSquared(dir); + } + else { + cursquaredist = 0; + } +#ifdef MISSIONPACK + if (gametype == GT_OBELISK) { + vec3_t target; + bot_goal_t *goal; + bsp_trace_t trace; + + if (BotTeam(bs) == TEAM_RED) + goal = &blueobelisk; + else + goal = &redobelisk; + //if the obelisk is visible + VectorCopy(goal->origin, target); + target[2] += 1; + BotAI_Trace(&trace, bs->eye, NULL, NULL, target, bs->client, CONTENTS_SOLID); + if (trace.fraction >= 1 || trace.ent == goal->entitynum) { + if (goal->entitynum == bs->enemy) { + return qfalse; + } + bs->enemy = goal->entitynum; + bs->enemysight_time = FloatTime(); + bs->enemysuicide = qfalse; + bs->enemydeath_time = 0; + bs->enemyvisible_time = FloatTime(); + return qtrue; + } + } +#endif + // + for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + + if (i == bs->client) continue; + //if it's the current enemy + if (i == curenemy) continue; + // + BotEntityInfo(i, &entinfo); + // + if (!entinfo.valid) continue; + //if the enemy isn't dead and the enemy isn't the bot self + if (EntityIsDead(&entinfo) || entinfo.number == bs->entitynum) continue; + //if the enemy is invisible and not shooting + if (EntityIsInvisible(&entinfo) && !EntityIsShooting(&entinfo)) { + continue; + } + //if not an easy fragger don't shoot at chatting players + if (easyfragger < 0.5 && EntityIsChatting(&entinfo)) continue; + // + if (lastteleport_time > FloatTime() - 3) { + VectorSubtract(entinfo.origin, lastteleport_origin, dir); + if (VectorLengthSquared(dir) < Square(70)) continue; + } + //calculate the distance towards the enemy + VectorSubtract(entinfo.origin, bs->origin, dir); + squaredist = VectorLengthSquared(dir); + //if this entity is not carrying a flag + if (!EntityCarriesFlag(&entinfo)) + { + //if this enemy is further away than the current one + if (curenemy >= 0 && squaredist > cursquaredist) continue; + } //end if + //if the bot has no + if (squaredist > Square(900.0 + alertness * 4000.0)) continue; + //if on the same team + if (BotSameTeam(bs, i)) continue; + //if the bot's health decreased or the enemy is shooting + if (curenemy < 0 && (healthdecrease || EntityIsShooting(&entinfo))) + f = 360; + else + f = 90 + 90 - (90 - (squaredist > Square(810) ? Square(810) : squaredist) / (810 * 9)); + //check if the enemy is visible + vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, f, i); + if (vis <= 0) continue; + //if the enemy is quite far away, not shooting and the bot is not damaged + if (curenemy < 0 && squaredist > Square(100) && !healthdecrease && !EntityIsShooting(&entinfo)) + { + //check if we can avoid this enemy + VectorSubtract(bs->origin, entinfo.origin, dir); + vectoangles(dir, angles); + //if the bot isn't in the fov of the enemy + if (!InFieldOfVision(entinfo.angles, 90, angles)) { + //update some stuff for this enemy + BotUpdateBattleInventory(bs, i); + //if the bot doesn't really want to fight + if (BotWantsToRetreat(bs)) continue; + } + } + //found an enemy + bs->enemy = entinfo.number; + if (curenemy >= 0) bs->enemysight_time = FloatTime() - 2; + else bs->enemysight_time = FloatTime(); + bs->enemysuicide = qfalse; + bs->enemydeath_time = 0; + bs->enemyvisible_time = FloatTime(); + return qtrue; + } + return qfalse; +} + +/* +================== +BotTeamFlagCarrierVisible +================== +*/ +int BotTeamFlagCarrierVisible(bot_state_t *bs) { + int i; + float vis; + aas_entityinfo_t entinfo; + + for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + if (i == bs->client) + continue; + // + BotEntityInfo(i, &entinfo); + //if this player is active + if (!entinfo.valid) + continue; + //if this player is carrying a flag + if (!EntityCarriesFlag(&entinfo)) + continue; + //if the flag carrier is not on the same team + if (!BotSameTeam(bs, i)) + continue; + //if the flag carrier is not visible + vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); + if (vis <= 0) + continue; + // + return i; + } + return -1; +} + +/* +================== +BotTeamFlagCarrier +================== +*/ +int BotTeamFlagCarrier(bot_state_t *bs) { + int i; + aas_entityinfo_t entinfo; + + for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + if (i == bs->client) + continue; + // + BotEntityInfo(i, &entinfo); + //if this player is active + if (!entinfo.valid) + continue; + //if this player is carrying a flag + if (!EntityCarriesFlag(&entinfo)) + continue; + //if the flag carrier is not on the same team + if (!BotSameTeam(bs, i)) + continue; + // + return i; + } + return -1; +} + +/* +================== +BotEnemyFlagCarrierVisible +================== +*/ +int BotEnemyFlagCarrierVisible(bot_state_t *bs) { + int i; + float vis; + aas_entityinfo_t entinfo; + + for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + if (i == bs->client) + continue; + // + BotEntityInfo(i, &entinfo); + //if this player is active + if (!entinfo.valid) + continue; + //if this player is carrying a flag + if (!EntityCarriesFlag(&entinfo)) + continue; + //if the flag carrier is on the same team + if (BotSameTeam(bs, i)) + continue; + //if the flag carrier is not visible + vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); + if (vis <= 0) + continue; + // + return i; + } + return -1; +} + +/* +================== +BotVisibleTeamMatesAndEnemies +================== +*/ +void BotVisibleTeamMatesAndEnemies(bot_state_t *bs, int *teammates, int *enemies, float range) { + int i; + float vis; + aas_entityinfo_t entinfo; + vec3_t dir; + + if (teammates) + *teammates = 0; + if (enemies) + *enemies = 0; + for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + if (i == bs->client) + continue; + // + BotEntityInfo(i, &entinfo); + //if this player is active + if (!entinfo.valid) + continue; + //if this player is carrying a flag + if (!EntityCarriesFlag(&entinfo)) + continue; + //if not within range + VectorSubtract(entinfo.origin, bs->origin, dir); + if (VectorLengthSquared(dir) > Square(range)) + continue; + //if the flag carrier is not visible + vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); + if (vis <= 0) + continue; + //if the flag carrier is on the same team + if (BotSameTeam(bs, i)) { + if (teammates) + (*teammates)++; + } + else { + if (enemies) + (*enemies)++; + } + } +} + +#ifdef MISSIONPACK +/* +================== +BotTeamCubeCarrierVisible +================== +*/ +int BotTeamCubeCarrierVisible(bot_state_t *bs) { + int i; + float vis; + aas_entityinfo_t entinfo; + + for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + if (i == bs->client) continue; + // + BotEntityInfo(i, &entinfo); + //if this player is active + if (!entinfo.valid) continue; + //if this player is carrying a flag + if (!EntityCarriesCubes(&entinfo)) continue; + //if the flag carrier is not on the same team + if (!BotSameTeam(bs, i)) continue; + //if the flag carrier is not visible + vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); + if (vis <= 0) continue; + // + return i; + } + return -1; +} + +/* +================== +BotEnemyCubeCarrierVisible +================== +*/ +int BotEnemyCubeCarrierVisible(bot_state_t *bs) { + int i; + float vis; + aas_entityinfo_t entinfo; + + for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + if (i == bs->client) + continue; + // + BotEntityInfo(i, &entinfo); + //if this player is active + if (!entinfo.valid) + continue; + //if this player is carrying a flag + if (!EntityCarriesCubes(&entinfo)) continue; + //if the flag carrier is on the same team + if (BotSameTeam(bs, i)) + continue; + //if the flag carrier is not visible + vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); + if (vis <= 0) + continue; + // + return i; + } + return -1; +} +#endif + +/* +================== +BotAimAtEnemy +================== +*/ +void BotAimAtEnemy(bot_state_t *bs) { + int i, enemyvisible; + float dist, f, aim_skill, aim_accuracy, speed, reactiontime; + vec3_t dir, bestorigin, end, start, groundtarget, cmdmove, enemyvelocity; + vec3_t mins = {-4,-4,-4}, maxs = {4, 4, 4}; + weaponinfo_t wi; + aas_entityinfo_t entinfo; + bot_goal_t goal; + bsp_trace_t trace; + vec3_t target; + + //if the bot has no enemy + if (bs->enemy < 0) { + return; + } + //get the enemy entity information + BotEntityInfo(bs->enemy, &entinfo); + //if this is not a player (should be an obelisk) + if (bs->enemy >= MAX_CLIENTS) { + //if the obelisk is visible + VectorCopy(entinfo.origin, target); +#ifdef MISSIONPACK + // if attacking an obelisk + if ( bs->enemy == redobelisk.entitynum || + bs->enemy == blueobelisk.entitynum ) { + target[2] += 32; + } +#endif + //aim at the obelisk + VectorSubtract(target, bs->eye, dir); + vectoangles(dir, bs->ideal_viewangles); + //set the aim target before trying to attack + VectorCopy(target, bs->aimtarget); + return; + } + // + //BotAI_Print(PRT_MESSAGE, "client %d: aiming at client %d\n", bs->entitynum, bs->enemy); + // + aim_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL, 0, 1); + aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY, 0, 1); + // + if (aim_skill > 0.95) { + //don't aim too early + reactiontime = 0.5 * trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_REACTIONTIME, 0, 1); + if (bs->enemysight_time > FloatTime() - reactiontime) return; + if (bs->teleport_time > FloatTime() - reactiontime) return; + } + + //get the weapon information + trap_BotGetWeaponInfo(bs->ws, bs->weaponnum, &wi); + //get the weapon specific aim accuracy and or aim skill + if (wi.number == WP_MACHINEGUN) { + aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_MACHINEGUN, 0, 1); + } + else if (wi.number == WP_SHOTGUN) { + aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_SHOTGUN, 0, 1); + } + else if (wi.number == WP_GRENADE_LAUNCHER) { + aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_GRENADELAUNCHER, 0, 1); + aim_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL_GRENADELAUNCHER, 0, 1); + } + else if (wi.number == WP_ROCKET_LAUNCHER) { + aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_ROCKETLAUNCHER, 0, 1); + aim_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL_ROCKETLAUNCHER, 0, 1); + } + else if (wi.number == WP_LIGHTNING) { + aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_LIGHTNING, 0, 1); + } + else if (wi.number == WP_RAILGUN) { + aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_RAILGUN, 0, 1); + } + else if (wi.number == WP_PLASMAGUN) { + aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_PLASMAGUN, 0, 1); + aim_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL_PLASMAGUN, 0, 1); + } + else if (wi.number == WP_BFG) { + aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_BFG10K, 0, 1); + aim_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL_BFG10K, 0, 1); + } + // + if (aim_accuracy <= 0) aim_accuracy = 0.0001f; + //get the enemy entity information + BotEntityInfo(bs->enemy, &entinfo); + //if the enemy is invisible then shoot crappy most of the time + if (EntityIsInvisible(&entinfo)) { + if (random() > 0.1) aim_accuracy *= 0.4f; + } + // + VectorSubtract(entinfo.origin, entinfo.lastvisorigin, enemyvelocity); + VectorScale(enemyvelocity, 1 / entinfo.update_time, enemyvelocity); + //enemy origin and velocity is remembered every 0.5 seconds + if (bs->enemyposition_time < FloatTime()) { + // + bs->enemyposition_time = FloatTime() + 0.5; + VectorCopy(enemyvelocity, bs->enemyvelocity); + VectorCopy(entinfo.origin, bs->enemyorigin); + } + //if not extremely skilled + if (aim_skill < 0.9) { + VectorSubtract(entinfo.origin, bs->enemyorigin, dir); + //if the enemy moved a bit + if (VectorLengthSquared(dir) > Square(48)) { + //if the enemy changed direction + if (DotProduct(bs->enemyvelocity, enemyvelocity) < 0) { + //aim accuracy should be worse now + aim_accuracy *= 0.7f; + } + } + } + //check visibility of enemy + enemyvisible = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy); + //if the enemy is visible + if (enemyvisible) { + // + VectorCopy(entinfo.origin, bestorigin); + bestorigin[2] += 8; + //get the start point shooting from + //NOTE: the x and y projectile start offsets are ignored + VectorCopy(bs->origin, start); + start[2] += bs->cur_ps.viewheight; + start[2] += wi.offset[2]; + // + BotAI_Trace(&trace, start, mins, maxs, bestorigin, bs->entitynum, MASK_SHOT); + //if the enemy is NOT hit + if (trace.fraction <= 1 && trace.ent != entinfo.number) { + bestorigin[2] += 16; + } + //if it is not an instant hit weapon the bot might want to predict the enemy + if (wi.speed) { + // + VectorSubtract(bestorigin, bs->origin, dir); + dist = VectorLength(dir); + VectorSubtract(entinfo.origin, bs->enemyorigin, dir); + //if the enemy is NOT pretty far away and strafing just small steps left and right + if (!(dist > 100 && VectorLengthSquared(dir) < Square(32))) { + //if skilled anough do exact prediction + if (aim_skill > 0.8 && + //if the weapon is ready to fire + bs->cur_ps.weaponstate == WEAPON_READY) { + aas_clientmove_t move; + vec3_t origin; + + VectorSubtract(entinfo.origin, bs->origin, dir); + //distance towards the enemy + dist = VectorLength(dir); + //direction the enemy is moving in + VectorSubtract(entinfo.origin, entinfo.lastvisorigin, dir); + // + VectorScale(dir, 1 / entinfo.update_time, dir); + // + VectorCopy(entinfo.origin, origin); + origin[2] += 1; + // + VectorClear(cmdmove); + //AAS_ClearShownDebugLines(); + trap_AAS_PredictClientMovement(&move, bs->enemy, origin, + PRESENCE_CROUCH, qfalse, + dir, cmdmove, 0, + dist * 10 / wi.speed, 0.1f, 0, 0, qfalse); + VectorCopy(move.endpos, bestorigin); + //BotAI_Print(PRT_MESSAGE, "%1.1f predicted speed = %f, frames = %f\n", FloatTime(), VectorLength(dir), dist * 10 / wi.speed); + } + //if not that skilled do linear prediction + else if (aim_skill > 0.4) { + VectorSubtract(entinfo.origin, bs->origin, dir); + //distance towards the enemy + dist = VectorLength(dir); + //direction the enemy is moving in + VectorSubtract(entinfo.origin, entinfo.lastvisorigin, dir); + dir[2] = 0; + // + speed = VectorNormalize(dir) / entinfo.update_time; + //botimport.Print(PRT_MESSAGE, "speed = %f, wi->speed = %f\n", speed, wi->speed); + //best spot to aim at + VectorMA(entinfo.origin, (dist / wi.speed) * speed, dir, bestorigin); + } + } + } + //if the projectile does radial damage + if (aim_skill > 0.6 && wi.proj.damagetype & DAMAGETYPE_RADIAL) { + //if the enemy isn't standing significantly higher than the bot + if (entinfo.origin[2] < bs->origin[2] + 16) { + //try to aim at the ground in front of the enemy + VectorCopy(entinfo.origin, end); + end[2] -= 64; + BotAI_Trace(&trace, entinfo.origin, NULL, NULL, end, entinfo.number, MASK_SHOT); + // + VectorCopy(bestorigin, groundtarget); + if (trace.startsolid) groundtarget[2] = entinfo.origin[2] - 16; + else groundtarget[2] = trace.endpos[2] - 8; + //trace a line from projectile start to ground target + BotAI_Trace(&trace, start, NULL, NULL, groundtarget, bs->entitynum, MASK_SHOT); + //if hitpoint is not vertically too far from the ground target + if (fabs(trace.endpos[2] - groundtarget[2]) < 50) { + VectorSubtract(trace.endpos, groundtarget, dir); + //if the hitpoint is near anough the ground target + if (VectorLengthSquared(dir) < Square(60)) { + VectorSubtract(trace.endpos, start, dir); + //if the hitpoint is far anough from the bot + if (VectorLengthSquared(dir) > Square(100)) { + //check if the bot is visible from the ground target + trace.endpos[2] += 1; + BotAI_Trace(&trace, trace.endpos, NULL, NULL, entinfo.origin, entinfo.number, MASK_SHOT); + if (trace.fraction >= 1) { + //botimport.Print(PRT_MESSAGE, "%1.1f aiming at ground\n", AAS_Time()); + VectorCopy(groundtarget, bestorigin); + } + } + } + } + } + } + bestorigin[0] += 20 * crandom() * (1 - aim_accuracy); + bestorigin[1] += 20 * crandom() * (1 - aim_accuracy); + bestorigin[2] += 10 * crandom() * (1 - aim_accuracy); + } + else { + // + VectorCopy(bs->lastenemyorigin, bestorigin); + bestorigin[2] += 8; + //if the bot is skilled anough + if (aim_skill > 0.5) { + //do prediction shots around corners + if (wi.number == WP_BFG || + wi.number == WP_ROCKET_LAUNCHER || + wi.number == WP_GRENADE_LAUNCHER) { + //create the chase goal + goal.entitynum = bs->client; + goal.areanum = bs->areanum; + VectorCopy(bs->eye, goal.origin); + VectorSet(goal.mins, -8, -8, -8); + VectorSet(goal.maxs, 8, 8, 8); + // + if (trap_BotPredictVisiblePosition(bs->lastenemyorigin, bs->lastenemyareanum, &goal, TFL_DEFAULT, target)) { + VectorSubtract(target, bs->eye, dir); + if (VectorLengthSquared(dir) > Square(80)) { + VectorCopy(target, bestorigin); + bestorigin[2] -= 20; + } + } + aim_accuracy = 1; + } + } + } + // + if (enemyvisible) { + BotAI_Trace(&trace, bs->eye, NULL, NULL, bestorigin, bs->entitynum, MASK_SHOT); + VectorCopy(trace.endpos, bs->aimtarget); + } + else { + VectorCopy(bestorigin, bs->aimtarget); + } + //get aim direction + VectorSubtract(bestorigin, bs->eye, dir); + // + if (wi.number == WP_MACHINEGUN || + wi.number == WP_SHOTGUN || + wi.number == WP_LIGHTNING || + wi.number == WP_RAILGUN) { + //distance towards the enemy + dist = VectorLength(dir); + if (dist > 150) dist = 150; + f = 0.6 + dist / 150 * 0.4; + aim_accuracy *= f; + } + //add some random stuff to the aim direction depending on the aim accuracy + if (aim_accuracy < 0.8) { + VectorNormalize(dir); + for (i = 0; i < 3; i++) dir[i] += 0.3 * crandom() * (1 - aim_accuracy); + } + //set the ideal view angles + vectoangles(dir, bs->ideal_viewangles); + //take the weapon spread into account for lower skilled bots + bs->ideal_viewangles[PITCH] += 6 * wi.vspread * crandom() * (1 - aim_accuracy); + bs->ideal_viewangles[PITCH] = AngleMod(bs->ideal_viewangles[PITCH]); + bs->ideal_viewangles[YAW] += 6 * wi.hspread * crandom() * (1 - aim_accuracy); + bs->ideal_viewangles[YAW] = AngleMod(bs->ideal_viewangles[YAW]); + //if the bots should be really challenging + if (bot_challenge.integer) { + //if the bot is really accurate and has the enemy in view for some time + if (aim_accuracy > 0.9 && bs->enemysight_time < FloatTime() - 1) { + //set the view angles directly + if (bs->ideal_viewangles[PITCH] > 180) bs->ideal_viewangles[PITCH] -= 360; + VectorCopy(bs->ideal_viewangles, bs->viewangles); + trap_EA_View(bs->client, bs->viewangles); + } + } +} + +/* +================== +BotCheckAttack +================== +*/ +void BotCheckAttack(bot_state_t *bs) { + float points, reactiontime, fov, firethrottle; + int attackentity; + bsp_trace_t bsptrace; + //float selfpreservation; + vec3_t forward, right, start, end, dir, angles; + weaponinfo_t wi; + bsp_trace_t trace; + aas_entityinfo_t entinfo; + vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8}; + + attackentity = bs->enemy; + // + BotEntityInfo(attackentity, &entinfo); + // if not attacking a player + if (attackentity >= MAX_CLIENTS) { +#ifdef MISSIONPACK + // if attacking an obelisk + if ( entinfo.number == redobelisk.entitynum || + entinfo.number == blueobelisk.entitynum ) { + // if obelisk is respawning return + if ( g_entities[entinfo.number].activator && + g_entities[entinfo.number].activator->s.frame == 2 ) { + return; + } + } +#endif + } + // + reactiontime = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_REACTIONTIME, 0, 1); + if (bs->enemysight_time > FloatTime() - reactiontime) return; + if (bs->teleport_time > FloatTime() - reactiontime) return; + //if changing weapons + if (bs->weaponchange_time > FloatTime() - 0.1) return; + //check fire throttle characteristic + if (bs->firethrottlewait_time > FloatTime()) return; + firethrottle = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_FIRETHROTTLE, 0, 1); + if (bs->firethrottleshoot_time < FloatTime()) { + if (random() > firethrottle) { + bs->firethrottlewait_time = FloatTime() + firethrottle; + bs->firethrottleshoot_time = 0; + } + else { + bs->firethrottleshoot_time = FloatTime() + 1 - firethrottle; + bs->firethrottlewait_time = 0; + } + } + // + // + VectorSubtract(bs->aimtarget, bs->eye, dir); + // + if (bs->weaponnum == WP_GAUNTLET) { + if (VectorLengthSquared(dir) > Square(60)) { + return; + } + } + if (VectorLengthSquared(dir) < Square(100)) + fov = 120; + else + fov = 50; + // + vectoangles(dir, angles); + if (!InFieldOfVision(bs->viewangles, fov, angles)) + return; + BotAI_Trace(&bsptrace, bs->eye, NULL, NULL, bs->aimtarget, bs->client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); + if (bsptrace.fraction < 1 && bsptrace.ent != attackentity) + return; + + //get the weapon info + trap_BotGetWeaponInfo(bs->ws, bs->weaponnum, &wi); + //get the start point shooting from + VectorCopy(bs->origin, start); + start[2] += bs->cur_ps.viewheight; + AngleVectors(bs->viewangles, forward, right, NULL); + start[0] += forward[0] * wi.offset[0] + right[0] * wi.offset[1]; + start[1] += forward[1] * wi.offset[0] + right[1] * wi.offset[1]; + start[2] += forward[2] * wi.offset[0] + right[2] * wi.offset[1] + wi.offset[2]; + //end point aiming at + VectorMA(start, 1000, forward, end); + //a little back to make sure not inside a very close enemy + VectorMA(start, -12, forward, start); + BotAI_Trace(&trace, start, mins, maxs, end, bs->entitynum, MASK_SHOT); + //if the entity is a client + if (trace.ent > 0 && trace.ent <= MAX_CLIENTS) { + if (trace.ent != attackentity) { + //if a teammate is hit + if (BotSameTeam(bs, trace.ent)) + return; + } + } + //if won't hit the enemy or not attacking a player (obelisk) + if (trace.ent != attackentity || attackentity >= MAX_CLIENTS) { + //if the projectile does radial damage + if (wi.proj.damagetype & DAMAGETYPE_RADIAL) { + if (trace.fraction * 1000 < wi.proj.radius) { + points = (wi.proj.damage - 0.5 * trace.fraction * 1000) * 0.5; + if (points > 0) { + return; + } + } + //FIXME: check if a teammate gets radial damage + } + } + //if fire has to be release to activate weapon + if (wi.flags & WFL_FIRERELEASED) { + if (bs->flags & BFL_ATTACKED) { + trap_EA_Attack(bs->client); + } + } + else { + trap_EA_Attack(bs->client); + } + bs->flags ^= BFL_ATTACKED; +} + +/* +================== +BotMapScripts +================== +*/ +void BotMapScripts(bot_state_t *bs) { + char info[1024]; + char mapname[128]; + int i, shootbutton; + float aim_accuracy; + aas_entityinfo_t entinfo; + vec3_t dir; + + trap_GetServerinfo(info, sizeof(info)); + + strncpy(mapname, Info_ValueForKey( info, "mapname" ), sizeof(mapname)-1); + mapname[sizeof(mapname)-1] = '\0'; + + if (!Q_stricmp(mapname, "q3tourney6")) { + vec3_t mins = {700, 204, 672}, maxs = {964, 468, 680}; + vec3_t buttonorg = {304, 352, 920}; + //NOTE: NEVER use the func_bobbing in q3tourney6 + bs->tfl &= ~TFL_FUNCBOB; + //if the bot is below the bounding box + if (bs->origin[0] > mins[0] && bs->origin[0] < maxs[0]) { + if (bs->origin[1] > mins[1] && bs->origin[1] < maxs[1]) { + if (bs->origin[2] < mins[2]) { + return; + } + } + } + shootbutton = qfalse; + //if an enemy is below this bounding box then shoot the button + for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + + if (i == bs->client) continue; + // + BotEntityInfo(i, &entinfo); + // + if (!entinfo.valid) continue; + //if the enemy isn't dead and the enemy isn't the bot self + if (EntityIsDead(&entinfo) || entinfo.number == bs->entitynum) continue; + // + if (entinfo.origin[0] > mins[0] && entinfo.origin[0] < maxs[0]) { + if (entinfo.origin[1] > mins[1] && entinfo.origin[1] < maxs[1]) { + if (entinfo.origin[2] < mins[2]) { + //if there's a team mate below the crusher + if (BotSameTeam(bs, i)) { + shootbutton = qfalse; + break; + } + else { + shootbutton = qtrue; + } + } + } + } + } + if (shootbutton) { + bs->flags |= BFL_IDEALVIEWSET; + VectorSubtract(buttonorg, bs->eye, dir); + vectoangles(dir, bs->ideal_viewangles); + aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY, 0, 1); + bs->ideal_viewangles[PITCH] += 8 * crandom() * (1 - aim_accuracy); + bs->ideal_viewangles[PITCH] = AngleMod(bs->ideal_viewangles[PITCH]); + bs->ideal_viewangles[YAW] += 8 * crandom() * (1 - aim_accuracy); + bs->ideal_viewangles[YAW] = AngleMod(bs->ideal_viewangles[YAW]); + // + if (InFieldOfVision(bs->viewangles, 20, bs->ideal_viewangles)) { + trap_EA_Attack(bs->client); + } + } + } + else if (!Q_stricmp(mapname, "mpq3tourney6")) { + //NOTE: NEVER use the func_bobbing in mpq3tourney6 + bs->tfl &= ~TFL_FUNCBOB; + } +} + +/* +================== +BotSetMovedir +================== +*/ +static vec3_t VEC_UP = {0, -1, 0}; +static vec3_t MOVEDIR_UP = {0, 0, 1}; +static vec3_t VEC_DOWN = {0, -2, 0}; +static vec3_t MOVEDIR_DOWN = {0, 0, -1}; + +void BotSetMovedir(vec3_t angles, vec3_t movedir) { + if (VectorCompare(angles, VEC_UP)) { + VectorCopy(MOVEDIR_UP, movedir); + } + else if (VectorCompare(angles, VEC_DOWN)) { + VectorCopy(MOVEDIR_DOWN, movedir); + } + else { + AngleVectors(angles, movedir, NULL, NULL); + } +} + +/* +================== +BotModelMinsMaxs + +this is ugly +================== +*/ +int BotModelMinsMaxs(int modelindex, int eType, int contents, vec3_t mins, vec3_t maxs) { + gentity_t *ent; + int i; + + ent = &g_entities[0]; + for (i = 0; i < level.num_entities; i++, ent++) { + if ( !ent->inuse ) { + continue; + } + if ( eType && ent->s.eType != eType) { + continue; + } + if ( contents && ent->r.contents != contents) { + continue; + } + if (ent->s.modelindex == modelindex) { + if (mins) + VectorAdd(ent->r.currentOrigin, ent->r.mins, mins); + if (maxs) + VectorAdd(ent->r.currentOrigin, ent->r.maxs, maxs); + return i; + } + } + if (mins) + VectorClear(mins); + if (maxs) + VectorClear(maxs); + return 0; +} + +/* +================== +BotFuncButtonGoal +================== +*/ +int BotFuncButtonActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *activategoal) { + int i, areas[10], numareas, modelindex, entitynum; + char model[128]; + float lip, dist, health, angle; + vec3_t size, start, end, mins, maxs, angles, points[10]; + vec3_t movedir, origin, goalorigin, bboxmins, bboxmaxs; + vec3_t extramins = {1, 1, 1}, extramaxs = {-1, -1, -1}; + bsp_trace_t bsptrace; + + activategoal->shoot = qfalse; + VectorClear(activategoal->target); + //create a bot goal towards the button + trap_AAS_ValueForBSPEpairKey(bspent, "model", model, sizeof(model)); + if (!*model) + return qfalse; + modelindex = atoi(model+1); + if (!modelindex) + return qfalse; + VectorClear(angles); + entitynum = BotModelMinsMaxs(modelindex, ET_MOVER, 0, mins, maxs); + //get the lip of the button + trap_AAS_FloatForBSPEpairKey(bspent, "lip", &lip); + if (!lip) lip = 4; + //get the move direction from the angle + trap_AAS_FloatForBSPEpairKey(bspent, "angle", &angle); + VectorSet(angles, 0, angle, 0); + BotSetMovedir(angles, movedir); + //button size + VectorSubtract(maxs, mins, size); + //button origin + VectorAdd(mins, maxs, origin); + VectorScale(origin, 0.5, origin); + //touch distance of the button + dist = fabs(movedir[0]) * size[0] + fabs(movedir[1]) * size[1] + fabs(movedir[2]) * size[2]; + dist *= 0.5; + // + trap_AAS_FloatForBSPEpairKey(bspent, "health", &health); + //if the button is shootable + if (health) { + //calculate the shoot target + VectorMA(origin, -dist, movedir, goalorigin); + // + VectorCopy(goalorigin, activategoal->target); + activategoal->shoot = qtrue; + // + BotAI_Trace(&bsptrace, bs->eye, NULL, NULL, goalorigin, bs->entitynum, MASK_SHOT); + // if the button is visible from the current position + if (bsptrace.fraction >= 1.0 || bsptrace.ent == entitynum) { + // + activategoal->goal.entitynum = entitynum; //NOTE: this is the entity number of the shootable button + activategoal->goal.number = 0; + activategoal->goal.flags = 0; + VectorCopy(bs->origin, activategoal->goal.origin); + activategoal->goal.areanum = bs->areanum; + VectorSet(activategoal->goal.mins, -8, -8, -8); + VectorSet(activategoal->goal.maxs, 8, 8, 8); + // + return qtrue; + } + else { + //create a goal from where the button is visible and shoot at the button from there + //add bounding box size to the dist + trap_AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bboxmins, bboxmaxs); + for (i = 0; i < 3; i++) { + if (movedir[i] < 0) dist += fabs(movedir[i]) * fabs(bboxmaxs[i]); + else dist += fabs(movedir[i]) * fabs(bboxmins[i]); + } + //calculate the goal origin + VectorMA(origin, -dist, movedir, goalorigin); + // + VectorCopy(goalorigin, start); + start[2] += 24; + VectorCopy(start, end); + end[2] -= 512; + numareas = trap_AAS_TraceAreas(start, end, areas, points, 10); + // + for (i = numareas-1; i >= 0; i--) { + if (trap_AAS_AreaReachability(areas[i])) { + break; + } + } + if (i < 0) { + // FIXME: trace forward and maybe in other directions to find a valid area + } + if (i >= 0) { + // + VectorCopy(points[i], activategoal->goal.origin); + activategoal->goal.areanum = areas[i]; + VectorSet(activategoal->goal.mins, 8, 8, 8); + VectorSet(activategoal->goal.maxs, -8, -8, -8); + // + for (i = 0; i < 3; i++) + { + if (movedir[i] < 0) activategoal->goal.maxs[i] += fabs(movedir[i]) * fabs(extramaxs[i]); + else activategoal->goal.mins[i] += fabs(movedir[i]) * fabs(extramins[i]); + } //end for + // + activategoal->goal.entitynum = entitynum; + activategoal->goal.number = 0; + activategoal->goal.flags = 0; + return qtrue; + } + } + return qfalse; + } + else { + //add bounding box size to the dist + trap_AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bboxmins, bboxmaxs); + for (i = 0; i < 3; i++) { + if (movedir[i] < 0) dist += fabs(movedir[i]) * fabs(bboxmaxs[i]); + else dist += fabs(movedir[i]) * fabs(bboxmins[i]); + } + //calculate the goal origin + VectorMA(origin, -dist, movedir, goalorigin); + // + VectorCopy(goalorigin, start); + start[2] += 24; + VectorCopy(start, end); + end[2] -= 100; + numareas = trap_AAS_TraceAreas(start, end, areas, NULL, 10); + // + for (i = 0; i < numareas; i++) { + if (trap_AAS_AreaReachability(areas[i])) { + break; + } + } + if (i < numareas) { + // + VectorCopy(origin, activategoal->goal.origin); + activategoal->goal.areanum = areas[i]; + VectorSubtract(mins, origin, activategoal->goal.mins); + VectorSubtract(maxs, origin, activategoal->goal.maxs); + // + for (i = 0; i < 3; i++) + { + if (movedir[i] < 0) activategoal->goal.maxs[i] += fabs(movedir[i]) * fabs(extramaxs[i]); + else activategoal->goal.mins[i] += fabs(movedir[i]) * fabs(extramins[i]); + } //end for + // + activategoal->goal.entitynum = entitynum; + activategoal->goal.number = 0; + activategoal->goal.flags = 0; + return qtrue; + } + } + return qfalse; +} + +/* +================== +BotFuncDoorGoal +================== +*/ +int BotFuncDoorActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *activategoal) { + int modelindex, entitynum; + char model[MAX_INFO_STRING]; + vec3_t mins, maxs, origin, angles; + + //shoot at the shootable door + trap_AAS_ValueForBSPEpairKey(bspent, "model", model, sizeof(model)); + if (!*model) + return qfalse; + modelindex = atoi(model+1); + if (!modelindex) + return qfalse; + VectorClear(angles); + entitynum = BotModelMinsMaxs(modelindex, ET_MOVER, 0, mins, maxs); + //door origin + VectorAdd(mins, maxs, origin); + VectorScale(origin, 0.5, origin); + VectorCopy(origin, activategoal->target); + activategoal->shoot = qtrue; + // + activategoal->goal.entitynum = entitynum; //NOTE: this is the entity number of the shootable door + activategoal->goal.number = 0; + activategoal->goal.flags = 0; + VectorCopy(bs->origin, activategoal->goal.origin); + activategoal->goal.areanum = bs->areanum; + VectorSet(activategoal->goal.mins, -8, -8, -8); + VectorSet(activategoal->goal.maxs, 8, 8, 8); + return qtrue; +} + +/* +================== +BotTriggerMultipleGoal +================== +*/ +int BotTriggerMultipleActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *activategoal) { + int i, areas[10], numareas, modelindex, entitynum; + char model[128]; + vec3_t start, end, mins, maxs, angles; + vec3_t origin, goalorigin; + + activategoal->shoot = qfalse; + VectorClear(activategoal->target); + //create a bot goal towards the trigger + trap_AAS_ValueForBSPEpairKey(bspent, "model", model, sizeof(model)); + if (!*model) + return qfalse; + modelindex = atoi(model+1); + if (!modelindex) + return qfalse; + VectorClear(angles); + entitynum = BotModelMinsMaxs(modelindex, 0, CONTENTS_TRIGGER, mins, maxs); + //trigger origin + VectorAdd(mins, maxs, origin); + VectorScale(origin, 0.5, origin); + VectorCopy(origin, goalorigin); + // + VectorCopy(goalorigin, start); + start[2] += 24; + VectorCopy(start, end); + end[2] -= 100; + numareas = trap_AAS_TraceAreas(start, end, areas, NULL, 10); + // + for (i = 0; i < numareas; i++) { + if (trap_AAS_AreaReachability(areas[i])) { + break; + } + } + if (i < numareas) { + VectorCopy(origin, activategoal->goal.origin); + activategoal->goal.areanum = areas[i]; + VectorSubtract(mins, origin, activategoal->goal.mins); + VectorSubtract(maxs, origin, activategoal->goal.maxs); + // + activategoal->goal.entitynum = entitynum; + activategoal->goal.number = 0; + activategoal->goal.flags = 0; + return qtrue; + } + return qfalse; +} + +/* +================== +BotPopFromActivateGoalStack +================== +*/ +int BotPopFromActivateGoalStack(bot_state_t *bs) { + if (!bs->activatestack) + return qfalse; + BotEnableActivateGoalAreas(bs->activatestack, qtrue); + bs->activatestack->inuse = qfalse; + bs->activatestack->justused_time = FloatTime(); + bs->activatestack = bs->activatestack->next; + return qtrue; +} + +/* +================== +BotPushOntoActivateGoalStack +================== +*/ +int BotPushOntoActivateGoalStack(bot_state_t *bs, bot_activategoal_t *activategoal) { + int i, best; + float besttime; + + best = -1; + besttime = FloatTime() + 9999; + // + for (i = 0; i < MAX_ACTIVATESTACK; i++) { + if (!bs->activategoalheap[i].inuse) { + if (bs->activategoalheap[i].justused_time < besttime) { + besttime = bs->activategoalheap[i].justused_time; + best = i; + } + } + } + if (best != -1) { + memcpy(&bs->activategoalheap[best], activategoal, sizeof(bot_activategoal_t)); + bs->activategoalheap[best].inuse = qtrue; + bs->activategoalheap[best].next = bs->activatestack; + bs->activatestack = &bs->activategoalheap[best]; + return qtrue; + } + return qfalse; +} + +/* +================== +BotClearActivateGoalStack +================== +*/ +void BotClearActivateGoalStack(bot_state_t *bs) { + while(bs->activatestack) + BotPopFromActivateGoalStack(bs); +} + +/* +================== +BotEnableActivateGoalAreas +================== +*/ +void BotEnableActivateGoalAreas(bot_activategoal_t *activategoal, int enable) { + int i; + + if (activategoal->areasdisabled == !enable) + return; + for (i = 0; i < activategoal->numareas; i++) + trap_AAS_EnableRoutingArea( activategoal->areas[i], enable ); + activategoal->areasdisabled = !enable; +} + +/* +================== +BotIsGoingToActivateEntity +================== +*/ +int BotIsGoingToActivateEntity(bot_state_t *bs, int entitynum) { + bot_activategoal_t *a; + int i; + + for (a = bs->activatestack; a; a = a->next) { + if (a->time < FloatTime()) + continue; + if (a->goal.entitynum == entitynum) + return qtrue; + } + for (i = 0; i < MAX_ACTIVATESTACK; i++) { + if (bs->activategoalheap[i].inuse) + continue; + // + if (bs->activategoalheap[i].goal.entitynum == entitynum) { + // if the bot went for this goal less than 2 seconds ago + if (bs->activategoalheap[i].justused_time > FloatTime() - 2) + return qtrue; + } + } + return qfalse; +} + +/* +================== +BotGetActivateGoal + + returns the number of the bsp entity to activate + goal->entitynum will be set to the game entity to activate +================== +*/ +//#define OBSTACLEDEBUG + +int BotGetActivateGoal(bot_state_t *bs, int entitynum, bot_activategoal_t *activategoal) { + int i, ent, cur_entities[10], spawnflags, modelindex, areas[MAX_ACTIVATEAREAS*2], numareas, t; + char model[MAX_INFO_STRING], tmpmodel[128]; + char target[128], classname[128]; + float health; + char targetname[10][128]; + aas_entityinfo_t entinfo; + aas_areainfo_t areainfo; + vec3_t origin, angles, absmins, absmaxs; + + memset(activategoal, 0, sizeof(bot_activategoal_t)); + BotEntityInfo(entitynum, &entinfo); + Com_sprintf(model, sizeof( model ), "*%d", entinfo.modelindex); + for (ent = trap_AAS_NextBSPEntity(0); ent; ent = trap_AAS_NextBSPEntity(ent)) { + if (!trap_AAS_ValueForBSPEpairKey(ent, "model", tmpmodel, sizeof(tmpmodel))) continue; + if (!strcmp(model, tmpmodel)) break; + } + if (!ent) { + BotAI_Print(PRT_ERROR, "BotGetActivateGoal: no entity found with model %s\n", model); + return 0; + } + trap_AAS_ValueForBSPEpairKey(ent, "classname", classname, sizeof(classname)); + if (!*classname) { + BotAI_Print(PRT_ERROR, "BotGetActivateGoal: entity with model %s has no classname\n", model); + return 0; + } + //if it is a door + if (!strcmp(classname, "func_door")) { + if (trap_AAS_FloatForBSPEpairKey(ent, "health", &health)) { + //if the door has health then the door must be shot to open + if (health) { + BotFuncDoorActivateGoal(bs, ent, activategoal); + return ent; + } + } + // + trap_AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); + // if the door starts open then just wait for the door to return + if ( spawnflags & 1 ) + return 0; + //get the door origin + if (!trap_AAS_VectorForBSPEpairKey(ent, "origin", origin)) { + VectorClear(origin); + } + //if the door is open or opening already + if (!VectorCompare(origin, entinfo.origin)) + return 0; + // store all the areas the door is in + trap_AAS_ValueForBSPEpairKey(ent, "model", model, sizeof(model)); + if (*model) { + modelindex = atoi(model+1); + if (modelindex) { + VectorClear(angles); + BotModelMinsMaxs(modelindex, ET_MOVER, 0, absmins, absmaxs); + // + numareas = trap_AAS_BBoxAreas(absmins, absmaxs, areas, MAX_ACTIVATEAREAS*2); + // store the areas with reachabilities first + for (i = 0; i < numareas; i++) { + if (activategoal->numareas >= MAX_ACTIVATEAREAS) + break; + if ( !trap_AAS_AreaReachability(areas[i]) ) { + continue; + } + trap_AAS_AreaInfo(areas[i], &areainfo); + if (areainfo.contents & AREACONTENTS_MOVER) { + activategoal->areas[activategoal->numareas++] = areas[i]; + } + } + // store any remaining areas + for (i = 0; i < numareas; i++) { + if (activategoal->numareas >= MAX_ACTIVATEAREAS) + break; + if ( trap_AAS_AreaReachability(areas[i]) ) { + continue; + } + trap_AAS_AreaInfo(areas[i], &areainfo); + if (areainfo.contents & AREACONTENTS_MOVER) { + activategoal->areas[activategoal->numareas++] = areas[i]; + } + } + } + } + } + // if the bot is blocked by or standing on top of a button + if (!strcmp(classname, "func_button")) { + return 0; + } + // get the targetname so we can find an entity with a matching target + if (!trap_AAS_ValueForBSPEpairKey(ent, "targetname", targetname[0], sizeof(targetname[0]))) { + if (bot_developer.integer) { + BotAI_Print(PRT_ERROR, "BotGetActivateGoal: entity with model \"%s\" has no targetname\n", model); + } + return 0; + } + // allow tree-like activation + cur_entities[0] = trap_AAS_NextBSPEntity(0); + for (i = 0; i >= 0 && i < 10;) { + for (ent = cur_entities[i]; ent; ent = trap_AAS_NextBSPEntity(ent)) { + if (!trap_AAS_ValueForBSPEpairKey(ent, "target", target, sizeof(target))) continue; + if (!strcmp(targetname[i], target)) { + cur_entities[i] = trap_AAS_NextBSPEntity(ent); + break; + } + } + if (!ent) { + if (bot_developer.integer) { + BotAI_Print(PRT_ERROR, "BotGetActivateGoal: no entity with target \"%s\"\n", targetname[i]); + } + i--; + continue; + } + if (!trap_AAS_ValueForBSPEpairKey(ent, "classname", classname, sizeof(classname))) { + if (bot_developer.integer) { + BotAI_Print(PRT_ERROR, "BotGetActivateGoal: entity with target \"%s\" has no classname\n", targetname[i]); + } + continue; + } + // BSP button model + if (!strcmp(classname, "func_button")) { + // + if (!BotFuncButtonActivateGoal(bs, ent, activategoal)) + continue; + // if the bot tries to activate this button already + if ( bs->activatestack && bs->activatestack->inuse && + bs->activatestack->goal.entitynum == activategoal->goal.entitynum && + bs->activatestack->time > FloatTime() && + bs->activatestack->start_time < FloatTime() - 2) + continue; + // if the bot is in a reachability area + if ( trap_AAS_AreaReachability(bs->areanum) ) { + // disable all areas the blocking entity is in + BotEnableActivateGoalAreas( activategoal, qfalse ); + // + t = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, activategoal->goal.areanum, bs->tfl); + // if the button is not reachable + if (!t) { + continue; + } + activategoal->time = FloatTime() + t * 0.01 + 5; + } + return ent; + } + // invisible trigger multiple box + else if (!strcmp(classname, "trigger_multiple")) { + // + if (!BotTriggerMultipleActivateGoal(bs, ent, activategoal)) + continue; + // if the bot tries to activate this trigger already + if ( bs->activatestack && bs->activatestack->inuse && + bs->activatestack->goal.entitynum == activategoal->goal.entitynum && + bs->activatestack->time > FloatTime() && + bs->activatestack->start_time < FloatTime() - 2) + continue; + // if the bot is in a reachability area + if ( trap_AAS_AreaReachability(bs->areanum) ) { + // disable all areas the blocking entity is in + BotEnableActivateGoalAreas( activategoal, qfalse ); + // + t = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, activategoal->goal.areanum, bs->tfl); + // if the trigger is not reachable + if (!t) { + continue; + } + activategoal->time = FloatTime() + t * 0.01 + 5; + } + return ent; + } + else if (!strcmp(classname, "func_timer")) { + // just skip the func_timer + continue; + } + // the actual button or trigger might be linked through a target_relay or target_delay + else if (!strcmp(classname, "target_relay") || !strcmp(classname, "target_delay")) { + if (trap_AAS_ValueForBSPEpairKey(ent, "targetname", targetname[i+1], sizeof(targetname[0]))) { + i++; + cur_entities[i] = trap_AAS_NextBSPEntity(0); + } + } + } +#ifdef OBSTACLEDEBUG + BotAI_Print(PRT_ERROR, "BotGetActivateGoal: no valid activator for entity with target \"%s\"\n", targetname[0]); +#endif + return 0; +} + +/* +================== +BotGoForActivateGoal +================== +*/ +int BotGoForActivateGoal(bot_state_t *bs, bot_activategoal_t *activategoal) { + aas_entityinfo_t activateinfo; + + activategoal->inuse = qtrue; + if (!activategoal->time) + activategoal->time = FloatTime() + 10; + activategoal->start_time = FloatTime(); + BotEntityInfo(activategoal->goal.entitynum, &activateinfo); + VectorCopy(activateinfo.origin, activategoal->origin); + // + if (BotPushOntoActivateGoalStack(bs, activategoal)) { + // enter the activate entity AI node + AIEnter_Seek_ActivateEntity(bs, "BotGoForActivateGoal"); + return qtrue; + } + else { + // enable any routing areas that were disabled + BotEnableActivateGoalAreas(activategoal, qtrue); + return qfalse; + } +} + +/* +================== +BotPrintActivateGoalInfo +================== +*/ +void BotPrintActivateGoalInfo(bot_state_t *bs, bot_activategoal_t *activategoal, int bspent) { + char netname[MAX_NETNAME]; + char classname[128]; + char buf[128]; + + ClientName(bs->client, netname, sizeof(netname)); + trap_AAS_ValueForBSPEpairKey(bspent, "classname", classname, sizeof(classname)); + if (activategoal->shoot) { + Com_sprintf(buf, sizeof(buf), "%s: I have to shoot at a %s from %1.1f %1.1f %1.1f in area %d\n", + netname, classname, + activategoal->goal.origin[0], + activategoal->goal.origin[1], + activategoal->goal.origin[2], + activategoal->goal.areanum); + } + else { + Com_sprintf(buf, sizeof(buf), "%s: I have to activate a %s at %1.1f %1.1f %1.1f in area %d\n", + netname, classname, + activategoal->goal.origin[0], + activategoal->goal.origin[1], + activategoal->goal.origin[2], + activategoal->goal.areanum); + } + trap_EA_Say(bs->client, buf); +} + +/* +================== +BotRandomMove +================== +*/ +void BotRandomMove(bot_state_t *bs, bot_moveresult_t *moveresult) { + vec3_t dir, angles; + + angles[0] = 0; + angles[1] = random() * 360; + angles[2] = 0; + AngleVectors(angles, dir, NULL, NULL); + + trap_BotMoveInDirection(bs->ms, dir, 400, MOVE_WALK); + + moveresult->failure = qfalse; + VectorCopy(dir, moveresult->movedir); +} + +/* +================== +BotAIBlocked + +Very basic handling of bots being blocked by other entities. +Check what kind of entity is blocking the bot and try to activate +it. If that's not an option then try to walk around or over the entity. +Before the bot ends in this part of the AI it should predict which doors to +open, which buttons to activate etc. +================== +*/ +void BotAIBlocked(bot_state_t *bs, bot_moveresult_t *moveresult, int activate) { + int movetype, bspent; + vec3_t hordir, start, end, mins, maxs, sideward, angles, up = {0, 0, 1}; + aas_entityinfo_t entinfo; + bot_activategoal_t activategoal; + + // if the bot is not blocked by anything + if (!moveresult->blocked) { + bs->notblocked_time = FloatTime(); + return; + } + // if stuck in a solid area + if ( moveresult->type == RESULTTYPE_INSOLIDAREA ) { + // move in a random direction in the hope to get out + BotRandomMove(bs, moveresult); + // + return; + } + // get info for the entity that is blocking the bot + BotEntityInfo(moveresult->blockentity, &entinfo); +#ifdef OBSTACLEDEBUG + ClientName(bs->client, netname, sizeof(netname)); + BotAI_Print(PRT_MESSAGE, "%s: I'm blocked by model %d\n", netname, entinfo.modelindex); +#endif // OBSTACLEDEBUG + // if blocked by a bsp model and the bot wants to activate it + if (activate && entinfo.modelindex > 0 && entinfo.modelindex <= max_bspmodelindex) { + // find the bsp entity which should be activated in order to get the blocking entity out of the way + bspent = BotGetActivateGoal(bs, entinfo.number, &activategoal); + if (bspent) { + // + if (bs->activatestack && !bs->activatestack->inuse) + bs->activatestack = NULL; + // if not already trying to activate this entity + if (!BotIsGoingToActivateEntity(bs, activategoal.goal.entitynum)) { + // + BotGoForActivateGoal(bs, &activategoal); + } + // if ontop of an obstacle or + // if the bot is not in a reachability area it'll still + // need some dynamic obstacle avoidance, otherwise return + if (!(moveresult->flags & MOVERESULT_ONTOPOFOBSTACLE) && + trap_AAS_AreaReachability(bs->areanum)) + return; + } + else { + // enable any routing areas that were disabled + BotEnableActivateGoalAreas(&activategoal, qtrue); + } + } + // just some basic dynamic obstacle avoidance code + hordir[0] = moveresult->movedir[0]; + hordir[1] = moveresult->movedir[1]; + hordir[2] = 0; + // if no direction just take a random direction + if (VectorNormalize(hordir) < 0.1) { + VectorSet(angles, 0, 360 * random(), 0); + AngleVectors(angles, hordir, NULL, NULL); + } + // + //if (moveresult->flags & MOVERESULT_ONTOPOFOBSTACLE) movetype = MOVE_JUMP; + //else + movetype = MOVE_WALK; + // if there's an obstacle at the bot's feet and head then + // the bot might be able to crouch through + VectorCopy(bs->origin, start); + start[2] += 18; + VectorMA(start, 5, hordir, end); + VectorSet(mins, -16, -16, -24); + VectorSet(maxs, 16, 16, 4); + // + //bsptrace = AAS_Trace(start, mins, maxs, end, bs->entitynum, MASK_PLAYERSOLID); + //if (bsptrace.fraction >= 1) movetype = MOVE_CROUCH; + // get the sideward vector + CrossProduct(hordir, up, sideward); + // + if (bs->flags & BFL_AVOIDRIGHT) VectorNegate(sideward, sideward); + // try to crouch straight forward? + if (movetype != MOVE_CROUCH || !trap_BotMoveInDirection(bs->ms, hordir, 400, movetype)) { + // perform the movement + if (!trap_BotMoveInDirection(bs->ms, sideward, 400, movetype)) { + // flip the avoid direction flag + bs->flags ^= BFL_AVOIDRIGHT; + // flip the direction + // VectorNegate(sideward, sideward); + VectorMA(sideward, -1, hordir, sideward); + // move in the other direction + trap_BotMoveInDirection(bs->ms, sideward, 400, movetype); + } + } + // + if (bs->notblocked_time < FloatTime() - 0.4) { + // just reset goals and hope the bot will go into another direction? + // is this still needed?? + if (bs->ainode == AINode_Seek_NBG) bs->nbg_time = 0; + else if (bs->ainode == AINode_Seek_LTG) bs->ltg_time = 0; + } +} + +/* +================== +BotAIPredictObstacles + +Predict the route towards the goal and check if the bot +will be blocked by certain obstacles. When the bot has obstacles +on it's path the bot should figure out if they can be removed +by activating certain entities. +================== +*/ +int BotAIPredictObstacles(bot_state_t *bs, bot_goal_t *goal) { + int modelnum, entitynum, bspent; + bot_activategoal_t activategoal; + aas_predictroute_t route; + + if (!bot_predictobstacles.integer) + return qfalse; + + // always predict when the goal change or at regular intervals + if (bs->predictobstacles_goalareanum == goal->areanum && + bs->predictobstacles_time > FloatTime() - 6) { + return qfalse; + } + bs->predictobstacles_goalareanum = goal->areanum; + bs->predictobstacles_time = FloatTime(); + + // predict at most 100 areas or 10 seconds ahead + trap_AAS_PredictRoute(&route, bs->areanum, bs->origin, + goal->areanum, bs->tfl, 100, 1000, + RSE_USETRAVELTYPE|RSE_ENTERCONTENTS, + AREACONTENTS_MOVER, TFL_BRIDGE, 0); + // if bot has to travel through an area with a mover + if (route.stopevent & RSE_ENTERCONTENTS) { + // if the bot will run into a mover + if (route.endcontents & AREACONTENTS_MOVER) { + //NOTE: this only works with bspc 2.1 or higher + modelnum = (route.endcontents & AREACONTENTS_MODELNUM) >> AREACONTENTS_MODELNUMSHIFT; + if (modelnum) { + // + entitynum = BotModelMinsMaxs(modelnum, ET_MOVER, 0, NULL, NULL); + if (entitynum) { + //NOTE: BotGetActivateGoal already checks if the door is open or not + bspent = BotGetActivateGoal(bs, entitynum, &activategoal); + if (bspent) { + // + if (bs->activatestack && !bs->activatestack->inuse) + bs->activatestack = NULL; + // if not already trying to activate this entity + if (!BotIsGoingToActivateEntity(bs, activategoal.goal.entitynum)) { + // + //BotAI_Print(PRT_MESSAGE, "blocked by mover model %d, entity %d ?\n", modelnum, entitynum); + // + BotGoForActivateGoal(bs, &activategoal); + return qtrue; + } + else { + // enable any routing areas that were disabled + BotEnableActivateGoalAreas(&activategoal, qtrue); + } + } + } + } + } + } + else if (route.stopevent & RSE_USETRAVELTYPE) { + if (route.endtravelflags & TFL_BRIDGE) { + //FIXME: check if the bridge is available to travel over + } + } + return qfalse; +} + +/* +================== +BotCheckConsoleMessages +================== +*/ +void BotCheckConsoleMessages(bot_state_t *bs) { + char botname[MAX_NETNAME], message[MAX_MESSAGE_SIZE], netname[MAX_NETNAME], *ptr; + float chat_reply; + int context, handle; + bot_consolemessage_t m; + bot_match_t match; + + //the name of this bot + ClientName(bs->client, botname, sizeof(botname)); + // + while((handle = trap_BotNextConsoleMessage(bs->cs, &m)) != 0) { + //if the chat state is flooded with messages the bot will read them quickly + if (trap_BotNumConsoleMessages(bs->cs) < 10) { + //if it is a chat message the bot needs some time to read it + if (m.type == CMS_CHAT && m.time > FloatTime() - (1 + random())) break; + } + // + ptr = m.message; + //if it is a chat message then don't unify white spaces and don't + //replace synonyms in the netname + if (m.type == CMS_CHAT) { + // + if (trap_BotFindMatch(m.message, &match, MTCONTEXT_REPLYCHAT)) { + ptr = m.message + match.variables[MESSAGE].offset; + } + } + //unify the white spaces in the message + trap_UnifyWhiteSpaces(ptr); + //replace synonyms in the right context + context = BotSynonymContext(bs); + trap_BotReplaceSynonyms(ptr, context); + //if there's no match + if (!BotMatchMessage(bs, m.message)) { + //if it is a chat message + if (m.type == CMS_CHAT && !bot_nochat.integer) { + // + if (!trap_BotFindMatch(m.message, &match, MTCONTEXT_REPLYCHAT)) { + trap_BotRemoveConsoleMessage(bs->cs, handle); + continue; + } + //don't use eliza chats with team messages + if (match.subtype & ST_TEAM) { + trap_BotRemoveConsoleMessage(bs->cs, handle); + continue; + } + // + trap_BotMatchVariable(&match, NETNAME, netname, sizeof(netname)); + trap_BotMatchVariable(&match, MESSAGE, message, sizeof(message)); + //if this is a message from the bot self + if (bs->client == ClientFromName(netname)) { + trap_BotRemoveConsoleMessage(bs->cs, handle); + continue; + } + //unify the message + trap_UnifyWhiteSpaces(message); + // + trap_Cvar_Update(&bot_testrchat); + if (bot_testrchat.integer) { + // + trap_BotLibVarSet("bot_testrchat", "1"); + //if bot replies with a chat message + if (trap_BotReplyChat(bs->cs, message, context, CONTEXT_REPLY, + NULL, NULL, + NULL, NULL, + NULL, NULL, + botname, netname)) { + BotAI_Print(PRT_MESSAGE, "------------------------\n"); + } + else { + BotAI_Print(PRT_MESSAGE, "**** no valid reply ****\n"); + } + } + //if at a valid chat position and not chatting already and not in teamplay + else if (bs->ainode != AINode_Stand && BotValidChatPosition(bs) && !TeamPlayIsOn()) { + chat_reply = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_REPLY, 0, 1); + if (random() < 1.5 / (NumBots()+1) && random() < chat_reply) { + //if bot replies with a chat message + if (trap_BotReplyChat(bs->cs, message, context, CONTEXT_REPLY, + NULL, NULL, + NULL, NULL, + NULL, NULL, + botname, netname)) { + //remove the console message + trap_BotRemoveConsoleMessage(bs->cs, handle); + bs->stand_time = FloatTime() + BotChatTime(bs); + AIEnter_Stand(bs, "BotCheckConsoleMessages: reply chat"); + //EA_Say(bs->client, bs->cs.chatmessage); + break; + } + } + } + } + } + //remove the console message + trap_BotRemoveConsoleMessage(bs->cs, handle); + } +} + +/* +================== +BotCheckEvents +================== +*/ +void BotCheckForGrenades(bot_state_t *bs, entityState_t *state) { + // if this is not a grenade + if (state->eType != ET_MISSILE || state->weapon != WP_GRENADE_LAUNCHER) + return; + // try to avoid the grenade + trap_BotAddAvoidSpot(bs->ms, state->pos.trBase, 160, AVOID_ALWAYS); +} + +#ifdef MISSIONPACK +/* +================== +BotCheckForProxMines +================== +*/ +void BotCheckForProxMines(bot_state_t *bs, entityState_t *state) { + // if this is not a prox mine + if (state->eType != ET_MISSILE || state->weapon != WP_PROX_LAUNCHER) + return; + // if this prox mine is from someone on our own team + if (state->generic1 == BotTeam(bs)) + return; + // if the bot doesn't have a weapon to deactivate the mine + if (!(bs->inventory[INVENTORY_PLASMAGUN] > 0 && bs->inventory[INVENTORY_CELLS] > 0) && + !(bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 && bs->inventory[INVENTORY_ROCKETS] > 0) && + !(bs->inventory[INVENTORY_BFG10K] > 0 && bs->inventory[INVENTORY_BFGAMMO] > 0) ) { + return; + } + // try to avoid the prox mine + trap_BotAddAvoidSpot(bs->ms, state->pos.trBase, 160, AVOID_ALWAYS); + // + if (bs->numproxmines >= MAX_PROXMINES) + return; + bs->proxmines[bs->numproxmines] = state->number; + bs->numproxmines++; +} + +/* +================== +BotCheckForKamikazeBody +================== +*/ +void BotCheckForKamikazeBody(bot_state_t *bs, entityState_t *state) { + // if this entity is not wearing the kamikaze + if (!(state->eFlags & EF_KAMIKAZE)) + return; + // if this entity isn't dead + if (!(state->eFlags & EF_DEAD)) + return; + //remember this kamikaze body + bs->kamikazebody = state->number; +} +#endif + +/* +================== +BotCheckEvents +================== +*/ +void BotCheckEvents(bot_state_t *bs, entityState_t *state) { + int event; + char buf[128]; +#ifdef MISSIONPACK + aas_entityinfo_t entinfo; +#endif + + //NOTE: this sucks, we're accessing the gentity_t directly + //but there's no other fast way to do it right now + if (bs->entityeventTime[state->number] == g_entities[state->number].eventTime) { + return; + } + bs->entityeventTime[state->number] = g_entities[state->number].eventTime; + //if it's an event only entity + if (state->eType > ET_EVENTS) { + event = (state->eType - ET_EVENTS) & ~EV_EVENT_BITS; + } + else { + event = state->event & ~EV_EVENT_BITS; + } + // + switch(event) { + //client obituary event + case EV_OBITUARY: + { + int target, attacker, mod; + + target = state->otherEntityNum; + attacker = state->otherEntityNum2; + mod = state->eventParm; + // + if (target == bs->client) { + bs->botdeathtype = mod; + bs->lastkilledby = attacker; + // + if (target == attacker || + target == ENTITYNUM_NONE || + target == ENTITYNUM_WORLD) bs->botsuicide = qtrue; + else bs->botsuicide = qfalse; + // + bs->num_deaths++; + } + //else if this client was killed by the bot + else if (attacker == bs->client) { + bs->enemydeathtype = mod; + bs->lastkilledplayer = target; + bs->killedenemy_time = FloatTime(); + // + bs->num_kills++; + } + else if (attacker == bs->enemy && target == attacker) { + bs->enemysuicide = qtrue; + } + // +#ifdef MISSIONPACK + if (gametype == GT_1FCTF) { + // + BotEntityInfo(target, &entinfo); + if ( entinfo.powerups & ( 1 << PW_NEUTRALFLAG ) ) { + if (!BotSameTeam(bs, target)) { + bs->neutralflagstatus = 3; //enemy dropped the flag + bs->flagstatuschanged = qtrue; + } + } + } +#endif + break; + } + case EV_GLOBAL_SOUND: + { + if (state->eventParm < 0 || state->eventParm > MAX_SOUNDS) { + BotAI_Print(PRT_ERROR, "EV_GLOBAL_SOUND: eventParm (%d) out of range\n", state->eventParm); + break; + } + trap_GetConfigstring(CS_SOUNDS + state->eventParm, buf, sizeof(buf)); + /* + if (!strcmp(buf, "sound/teamplay/flagret_red.wav")) { + //red flag is returned + bs->redflagstatus = 0; + bs->flagstatuschanged = qtrue; + } + else if (!strcmp(buf, "sound/teamplay/flagret_blu.wav")) { + //blue flag is returned + bs->blueflagstatus = 0; + bs->flagstatuschanged = qtrue; + } + else*/ +#ifdef MISSIONPACK + if (!strcmp(buf, "sound/items/kamikazerespawn.wav" )) { + //the kamikaze respawned so dont avoid it + BotDontAvoid(bs, "Kamikaze"); + } + else +#endif + if (!strcmp(buf, "sound/items/poweruprespawn.wav")) { + //powerup respawned... go get it + BotGoForPowerups(bs); + } + break; + } + case EV_GLOBAL_TEAM_SOUND: + { + if (gametype == GT_CTF) { + switch(state->eventParm) { + case GTS_RED_CAPTURE: + bs->blueflagstatus = 0; + bs->redflagstatus = 0; + bs->flagstatuschanged = qtrue; + break; //see BotMatch_CTF + case GTS_BLUE_CAPTURE: + bs->blueflagstatus = 0; + bs->redflagstatus = 0; + bs->flagstatuschanged = qtrue; + break; //see BotMatch_CTF + case GTS_RED_RETURN: + //blue flag is returned + bs->blueflagstatus = 0; + bs->flagstatuschanged = qtrue; + break; + case GTS_BLUE_RETURN: + //red flag is returned + bs->redflagstatus = 0; + bs->flagstatuschanged = qtrue; + break; + case GTS_RED_TAKEN: + //blue flag is taken + bs->blueflagstatus = 1; + bs->flagstatuschanged = qtrue; + break; //see BotMatch_CTF + case GTS_BLUE_TAKEN: + //red flag is taken + bs->redflagstatus = 1; + bs->flagstatuschanged = qtrue; + break; //see BotMatch_CTF + } + } +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF) { + switch(state->eventParm) { + case GTS_RED_CAPTURE: + bs->neutralflagstatus = 0; + bs->flagstatuschanged = qtrue; + break; + case GTS_BLUE_CAPTURE: + bs->neutralflagstatus = 0; + bs->flagstatuschanged = qtrue; + break; + case GTS_RED_RETURN: + //flag has returned + bs->neutralflagstatus = 0; + bs->flagstatuschanged = qtrue; + break; + case GTS_BLUE_RETURN: + //flag has returned + bs->neutralflagstatus = 0; + bs->flagstatuschanged = qtrue; + break; + case GTS_RED_TAKEN: + bs->neutralflagstatus = BotTeam(bs) == TEAM_RED ? 2 : 1; //FIXME: check Team_TakeFlagSound in g_team.c + bs->flagstatuschanged = qtrue; + break; + case GTS_BLUE_TAKEN: + bs->neutralflagstatus = BotTeam(bs) == TEAM_BLUE ? 2 : 1; //FIXME: check Team_TakeFlagSound in g_team.c + bs->flagstatuschanged = qtrue; + break; + } + } +#endif + break; + } + case EV_PLAYER_TELEPORT_IN: + { + VectorCopy(state->origin, lastteleport_origin); + lastteleport_time = FloatTime(); + break; + } + case EV_GENERAL_SOUND: + { + //if this sound is played on the bot + if (state->number == bs->client) { + if (state->eventParm < 0 || state->eventParm > MAX_SOUNDS) { + BotAI_Print(PRT_ERROR, "EV_GENERAL_SOUND: eventParm (%d) out of range\n", state->eventParm); + break; + } + //check out the sound + trap_GetConfigstring(CS_SOUNDS + state->eventParm, buf, sizeof(buf)); + //if falling into a death pit + if (!strcmp(buf, "*falling1.wav")) { + //if the bot has a personal teleporter + if (bs->inventory[INVENTORY_TELEPORTER] > 0) { + //use the holdable item + trap_EA_Use(bs->client); + } + } + } + break; + } + case EV_FOOTSTEP: + case EV_FOOTSTEP_METAL: + case EV_FOOTSPLASH: + case EV_FOOTWADE: + case EV_SWIM: + case EV_FALL_SHORT: + case EV_FALL_MEDIUM: + case EV_FALL_FAR: + case EV_STEP_4: + case EV_STEP_8: + case EV_STEP_12: + case EV_STEP_16: + case EV_JUMP_PAD: + case EV_JUMP: + case EV_TAUNT: + case EV_WATER_TOUCH: + case EV_WATER_LEAVE: + case EV_WATER_UNDER: + case EV_WATER_CLEAR: + case EV_ITEM_PICKUP: + case EV_GLOBAL_ITEM_PICKUP: + case EV_NOAMMO: + case EV_CHANGE_WEAPON: + case EV_FIRE_WEAPON: + //FIXME: either add to sound queue or mark player as someone making noise + break; + case EV_USE_ITEM0: + case EV_USE_ITEM1: + case EV_USE_ITEM2: + case EV_USE_ITEM3: + case EV_USE_ITEM4: + case EV_USE_ITEM5: + case EV_USE_ITEM6: + case EV_USE_ITEM7: + case EV_USE_ITEM8: + case EV_USE_ITEM9: + case EV_USE_ITEM10: + case EV_USE_ITEM11: + case EV_USE_ITEM12: + case EV_USE_ITEM13: + case EV_USE_ITEM14: + break; + } +} + +/* +================== +BotCheckSnapshot +================== +*/ +void BotCheckSnapshot(bot_state_t *bs) { + int ent; + entityState_t state; + + //remove all avoid spots + trap_BotAddAvoidSpot(bs->ms, vec3_origin, 0, AVOID_CLEAR); + //reset kamikaze body + bs->kamikazebody = 0; + //reset number of proxmines + bs->numproxmines = 0; + // + ent = 0; + while( ( ent = BotAI_GetSnapshotEntity( bs->client, ent, &state ) ) != -1 ) { + //check the entity state for events + BotCheckEvents(bs, &state); + //check for grenades the bot should avoid + BotCheckForGrenades(bs, &state); + // +#ifdef MISSIONPACK + //check for proximity mines which the bot should deactivate + BotCheckForProxMines(bs, &state); + //check for dead bodies with the kamikaze effect which should be gibbed + BotCheckForKamikazeBody(bs, &state); +#endif + } + //check the player state for events + BotAI_GetEntityState(bs->client, &state); + //copy the player state events to the entity state + state.event = bs->cur_ps.externalEvent; + state.eventParm = bs->cur_ps.externalEventParm; + // + BotCheckEvents(bs, &state); +} + +/* +================== +BotCheckAir +================== +*/ +void BotCheckAir(bot_state_t *bs) { + if (bs->inventory[INVENTORY_ENVIRONMENTSUIT] <= 0) { + if (trap_AAS_PointContents(bs->eye) & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) { + return; + } + } + bs->lastair_time = FloatTime(); +} + +/* +================== +BotAlternateRoute +================== +*/ +bot_goal_t *BotAlternateRoute(bot_state_t *bs, bot_goal_t *goal) { + int t; + + // if the bot has an alternative route goal + if (bs->altroutegoal.areanum) { + // + if (bs->reachedaltroutegoal_time) + return goal; + // travel time towards alternative route goal + t = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, bs->altroutegoal.areanum, bs->tfl); + if (t && t < 20) { + //BotAI_Print(PRT_MESSAGE, "reached alternate route goal\n"); + bs->reachedaltroutegoal_time = FloatTime(); + } + memcpy(goal, &bs->altroutegoal, sizeof(bot_goal_t)); + return &bs->altroutegoal; + } + return goal; +} + +/* +================== +BotGetAlternateRouteGoal +================== +*/ +int BotGetAlternateRouteGoal(bot_state_t *bs, int base) { + aas_altroutegoal_t *altroutegoals; + bot_goal_t *goal; + int numaltroutegoals, rnd; + + if (base == TEAM_RED) { + altroutegoals = red_altroutegoals; + numaltroutegoals = red_numaltroutegoals; + } + else { + altroutegoals = blue_altroutegoals; + numaltroutegoals = blue_numaltroutegoals; + } + if (!numaltroutegoals) + return qfalse; + rnd = (float) random() * numaltroutegoals; + if (rnd >= numaltroutegoals) + rnd = numaltroutegoals-1; + goal = &bs->altroutegoal; + goal->areanum = altroutegoals[rnd].areanum; + VectorCopy(altroutegoals[rnd].origin, goal->origin); + VectorSet(goal->mins, -8, -8, -8); + VectorSet(goal->maxs, 8, 8, 8); + goal->entitynum = 0; + goal->iteminfo = 0; + goal->number = 0; + goal->flags = 0; + // + bs->reachedaltroutegoal_time = 0; + return qtrue; +} + +/* +================== +BotSetupAlternateRouteGoals +================== +*/ +void BotSetupAlternativeRouteGoals(void) { + + if (altroutegoals_setup) + return; +#ifdef MISSIONPACK + if (gametype == GT_CTF) { + if (trap_BotGetLevelItemGoal(-1, "Neutral Flag", &ctf_neutralflag) < 0) + BotAI_Print(PRT_WARNING, "no alt routes without Neutral Flag\n"); + if (ctf_neutralflag.areanum) { + // + red_numaltroutegoals = trap_AAS_AlternativeRouteGoals( + ctf_neutralflag.origin, ctf_neutralflag.areanum, + ctf_redflag.origin, ctf_redflag.areanum, TFL_DEFAULT, + red_altroutegoals, MAX_ALTROUTEGOALS, + ALTROUTEGOAL_CLUSTERPORTALS| + ALTROUTEGOAL_VIEWPORTALS); + blue_numaltroutegoals = trap_AAS_AlternativeRouteGoals( + ctf_neutralflag.origin, ctf_neutralflag.areanum, + ctf_blueflag.origin, ctf_blueflag.areanum, TFL_DEFAULT, + blue_altroutegoals, MAX_ALTROUTEGOALS, + ALTROUTEGOAL_CLUSTERPORTALS| + ALTROUTEGOAL_VIEWPORTALS); + } + } + else if (gametype == GT_1FCTF) { + // + red_numaltroutegoals = trap_AAS_AlternativeRouteGoals( + ctf_neutralflag.origin, ctf_neutralflag.areanum, + ctf_redflag.origin, ctf_redflag.areanum, TFL_DEFAULT, + red_altroutegoals, MAX_ALTROUTEGOALS, + ALTROUTEGOAL_CLUSTERPORTALS| + ALTROUTEGOAL_VIEWPORTALS); + blue_numaltroutegoals = trap_AAS_AlternativeRouteGoals( + ctf_neutralflag.origin, ctf_neutralflag.areanum, + ctf_blueflag.origin, ctf_blueflag.areanum, TFL_DEFAULT, + blue_altroutegoals, MAX_ALTROUTEGOALS, + ALTROUTEGOAL_CLUSTERPORTALS| + ALTROUTEGOAL_VIEWPORTALS); + } + else if (gametype == GT_OBELISK) { + if (trap_BotGetLevelItemGoal(-1, "Neutral Obelisk", &neutralobelisk) < 0) + BotAI_Print(PRT_WARNING, "Harvester without neutral obelisk\n"); + // + red_numaltroutegoals = trap_AAS_AlternativeRouteGoals( + neutralobelisk.origin, neutralobelisk.areanum, + redobelisk.origin, redobelisk.areanum, TFL_DEFAULT, + red_altroutegoals, MAX_ALTROUTEGOALS, + ALTROUTEGOAL_CLUSTERPORTALS| + ALTROUTEGOAL_VIEWPORTALS); + blue_numaltroutegoals = trap_AAS_AlternativeRouteGoals( + neutralobelisk.origin, neutralobelisk.areanum, + blueobelisk.origin, blueobelisk.areanum, TFL_DEFAULT, + blue_altroutegoals, MAX_ALTROUTEGOALS, + ALTROUTEGOAL_CLUSTERPORTALS| + ALTROUTEGOAL_VIEWPORTALS); + } + else if (gametype == GT_HARVESTER) { + // + red_numaltroutegoals = trap_AAS_AlternativeRouteGoals( + neutralobelisk.origin, neutralobelisk.areanum, + redobelisk.origin, redobelisk.areanum, TFL_DEFAULT, + red_altroutegoals, MAX_ALTROUTEGOALS, + ALTROUTEGOAL_CLUSTERPORTALS| + ALTROUTEGOAL_VIEWPORTALS); + blue_numaltroutegoals = trap_AAS_AlternativeRouteGoals( + neutralobelisk.origin, neutralobelisk.areanum, + blueobelisk.origin, blueobelisk.areanum, TFL_DEFAULT, + blue_altroutegoals, MAX_ALTROUTEGOALS, + ALTROUTEGOAL_CLUSTERPORTALS| + ALTROUTEGOAL_VIEWPORTALS); + } +#endif + altroutegoals_setup = qtrue; +} + +/* +================== +BotDeathmatchAI +================== +*/ +void BotDeathmatchAI(bot_state_t *bs, float thinktime) { + char gender[144], name[144], buf[144]; + char userinfo[MAX_INFO_STRING]; + int i; + + //if the bot has just been setup + if (bs->setupcount > 0) { + bs->setupcount--; + if (bs->setupcount > 0) return; + //get the gender characteristic + trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, sizeof(gender)); + //set the bot gender + trap_GetUserinfo(bs->client, userinfo, sizeof(userinfo)); + Info_SetValueForKey(userinfo, "sex", gender); + trap_SetUserinfo(bs->client, userinfo); + //set the team + if ( !bs->map_restart && g_gametype.integer != GT_TOURNAMENT ) { + Com_sprintf(buf, sizeof(buf), "team %s", bs->settings.team); + trap_EA_Command(bs->client, buf); + } + //set the chat gender + if (gender[0] == 'm') trap_BotSetChatGender(bs->cs, CHAT_GENDERMALE); + else if (gender[0] == 'f') trap_BotSetChatGender(bs->cs, CHAT_GENDERFEMALE); + else trap_BotSetChatGender(bs->cs, CHAT_GENDERLESS); + //set the chat name + ClientName(bs->client, name, sizeof(name)); + trap_BotSetChatName(bs->cs, name, bs->client); + // + bs->lastframe_health = bs->inventory[INVENTORY_HEALTH]; + bs->lasthitcount = bs->cur_ps.persistant[PERS_HITS]; + // + bs->setupcount = 0; + // + BotSetupAlternativeRouteGoals(); + } + //no ideal view set + bs->flags &= ~BFL_IDEALVIEWSET; + // + if (!BotIntermission(bs)) { + //set the teleport time + BotSetTeleportTime(bs); + //update some inventory values + BotUpdateInventory(bs); + //check out the snapshot + BotCheckSnapshot(bs); + //check for air + BotCheckAir(bs); + } + //check the console messages + BotCheckConsoleMessages(bs); + //if not in the intermission and not in observer mode + if (!BotIntermission(bs) && !BotIsObserver(bs)) { + //do team AI + BotTeamAI(bs); + } + //if the bot has no ai node + if (!bs->ainode) { + AIEnter_Seek_LTG(bs, "BotDeathmatchAI: no ai node"); + } + //if the bot entered the game less than 8 seconds ago + if (!bs->entergamechat && bs->entergame_time > FloatTime() - 8) { + if (BotChat_EnterGame(bs)) { + bs->stand_time = FloatTime() + BotChatTime(bs); + AIEnter_Stand(bs, "BotDeathmatchAI: chat enter game"); + } + bs->entergamechat = qtrue; + } + //reset the node switches from the previous frame + BotResetNodeSwitches(); + //execute AI nodes + for (i = 0; i < MAX_NODESWITCHES; i++) { + if (bs->ainode(bs)) break; + } + //if the bot removed itself :) + if (!bs->inuse) return; + //if the bot executed too many AI nodes + if (i >= MAX_NODESWITCHES) { + trap_BotDumpGoalStack(bs->gs); + trap_BotDumpAvoidGoals(bs->gs); + BotDumpNodeSwitches(bs); + ClientName(bs->client, name, sizeof(name)); + BotAI_Print(PRT_ERROR, "%s at %1.1f switched more than %d AI nodes\n", name, FloatTime(), MAX_NODESWITCHES); + } + // + bs->lastframe_health = bs->inventory[INVENTORY_HEALTH]; + bs->lasthitcount = bs->cur_ps.persistant[PERS_HITS]; +} + +/* +================== +BotSetEntityNumForGoalWithModel +================== +*/ +void BotSetEntityNumForGoalWithModel(bot_goal_t *goal, int eType, char *modelname) { + gentity_t *ent; + int i, modelindex; + vec3_t dir; + + modelindex = G_ModelIndex( modelname ); + ent = &g_entities[0]; + for (i = 0; i < level.num_entities; i++, ent++) { + if ( !ent->inuse ) { + continue; + } + if ( eType && ent->s.eType != eType) { + continue; + } + if (ent->s.modelindex != modelindex) { + continue; + } + VectorSubtract(goal->origin, ent->s.origin, dir); + if (VectorLengthSquared(dir) < Square(10)) { + goal->entitynum = i; + return; + } + } +} + +/* +================== +BotSetEntityNumForGoal +================== +*/ +void BotSetEntityNumForGoal(bot_goal_t *goal, char *classname) { + gentity_t *ent; + int i; + vec3_t dir; + + ent = &g_entities[0]; + for (i = 0; i < level.num_entities; i++, ent++) { + if ( !ent->inuse ) { + continue; + } + if ( !Q_stricmp(ent->classname, classname) ) { + continue; + } + VectorSubtract(goal->origin, ent->s.origin, dir); + if (VectorLengthSquared(dir) < Square(10)) { + goal->entitynum = i; + return; + } + } +} + +/* +================== +BotGoalForBSPEntity +================== +*/ +int BotGoalForBSPEntity( char *classname, bot_goal_t *goal ) { + char value[MAX_INFO_STRING]; + vec3_t origin, start, end; + int ent, numareas, areas[10]; + + memset(goal, 0, sizeof(bot_goal_t)); + for (ent = trap_AAS_NextBSPEntity(0); ent; ent = trap_AAS_NextBSPEntity(ent)) { + if (!trap_AAS_ValueForBSPEpairKey(ent, "classname", value, sizeof(value))) + continue; + if (!strcmp(value, classname)) { + if (!trap_AAS_VectorForBSPEpairKey(ent, "origin", origin)) + return qfalse; + VectorCopy(origin, goal->origin); + VectorCopy(origin, start); + start[2] -= 32; + VectorCopy(origin, end); + end[2] += 32; + numareas = trap_AAS_TraceAreas(start, end, areas, NULL, 10); + if (!numareas) + return qfalse; + goal->areanum = areas[0]; + return qtrue; + } + } + return qfalse; +} + +/* +================== +BotSetupDeathmatchAI +================== +*/ +void BotSetupDeathmatchAI(void) { + int ent, modelnum; + char model[128]; + + gametype = trap_Cvar_VariableIntegerValue("g_gametype"); + maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); + + trap_Cvar_Register(&bot_rocketjump, "bot_rocketjump", "1", 0); + trap_Cvar_Register(&bot_grapple, "bot_grapple", "0", 0); + trap_Cvar_Register(&bot_fastchat, "bot_fastchat", "0", 0); + trap_Cvar_Register(&bot_nochat, "bot_nochat", "0", 0); + trap_Cvar_Register(&bot_testrchat, "bot_testrchat", "0", 0); + trap_Cvar_Register(&bot_challenge, "bot_challenge", "0", 0); + trap_Cvar_Register(&bot_predictobstacles, "bot_predictobstacles", "1", 0); + trap_Cvar_Register(&g_spSkill, "g_spSkill", "2", 0); + // + if (gametype == GT_CTF) { + if (trap_BotGetLevelItemGoal(-1, "Red Flag", &ctf_redflag) < 0) + BotAI_Print(PRT_WARNING, "CTF without Red Flag\n"); + if (trap_BotGetLevelItemGoal(-1, "Blue Flag", &ctf_blueflag) < 0) + BotAI_Print(PRT_WARNING, "CTF without Blue Flag\n"); + } +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF) { + if (trap_BotGetLevelItemGoal(-1, "Neutral Flag", &ctf_neutralflag) < 0) + BotAI_Print(PRT_WARNING, "One Flag CTF without Neutral Flag\n"); + if (trap_BotGetLevelItemGoal(-1, "Red Flag", &ctf_redflag) < 0) + BotAI_Print(PRT_WARNING, "CTF without Red Flag\n"); + if (trap_BotGetLevelItemGoal(-1, "Blue Flag", &ctf_blueflag) < 0) + BotAI_Print(PRT_WARNING, "CTF without Blue Flag\n"); + } + else if (gametype == GT_OBELISK) { + if (trap_BotGetLevelItemGoal(-1, "Red Obelisk", &redobelisk) < 0) + BotAI_Print(PRT_WARNING, "Obelisk without red obelisk\n"); + BotSetEntityNumForGoal(&redobelisk, "team_redobelisk"); + if (trap_BotGetLevelItemGoal(-1, "Blue Obelisk", &blueobelisk) < 0) + BotAI_Print(PRT_WARNING, "Obelisk without blue obelisk\n"); + BotSetEntityNumForGoal(&blueobelisk, "team_blueobelisk"); + } + else if (gametype == GT_HARVESTER) { + if (trap_BotGetLevelItemGoal(-1, "Red Obelisk", &redobelisk) < 0) + BotAI_Print(PRT_WARNING, "Harvester without red obelisk\n"); + BotSetEntityNumForGoal(&redobelisk, "team_redobelisk"); + if (trap_BotGetLevelItemGoal(-1, "Blue Obelisk", &blueobelisk) < 0) + BotAI_Print(PRT_WARNING, "Harvester without blue obelisk\n"); + BotSetEntityNumForGoal(&blueobelisk, "team_blueobelisk"); + if (trap_BotGetLevelItemGoal(-1, "Neutral Obelisk", &neutralobelisk) < 0) + BotAI_Print(PRT_WARNING, "Harvester without neutral obelisk\n"); + BotSetEntityNumForGoal(&neutralobelisk, "team_neutralobelisk"); + } +#endif + + max_bspmodelindex = 0; + for (ent = trap_AAS_NextBSPEntity(0); ent; ent = trap_AAS_NextBSPEntity(ent)) { + if (!trap_AAS_ValueForBSPEpairKey(ent, "model", model, sizeof(model))) continue; + if (model[0] == '*') { + modelnum = atoi(model+1); + if (modelnum > max_bspmodelindex) + max_bspmodelindex = modelnum; + } + } + //initialize the waypoint heap + BotInitWaypoints(); +} + +/* +================== +BotShutdownDeathmatchAI +================== +*/ +void BotShutdownDeathmatchAI(void) { + altroutegoals_setup = qfalse; +} diff --git a/reaction/engine/code/game/ai_dmq3.h b/reaction/engine/code/game/ai_dmq3.h new file mode 100644 index 00000000..f6829bea --- /dev/null +++ b/reaction/engine/code/game/ai_dmq3.h @@ -0,0 +1,206 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: ai_dmq3.h + * + * desc: Quake3 bot AI + * + * $Archive: /source/code/botai/ai_chat.c $ + * + *****************************************************************************/ + +//setup the deathmatch AI +void BotSetupDeathmatchAI(void); +//shutdown the deathmatch AI +void BotShutdownDeathmatchAI(void); +//let the bot live within it's deathmatch AI net +void BotDeathmatchAI(bot_state_t *bs, float thinktime); +//free waypoints +void BotFreeWaypoints(bot_waypoint_t *wp); +//choose a weapon +void BotChooseWeapon(bot_state_t *bs); +//setup movement stuff +void BotSetupForMovement(bot_state_t *bs); +//update the inventory +void BotUpdateInventory(bot_state_t *bs); +//update the inventory during battle +void BotUpdateBattleInventory(bot_state_t *bs, int enemy); +//use holdable items during battle +void BotBattleUseItems(bot_state_t *bs); +//return true if the bot is dead +qboolean BotIsDead(bot_state_t *bs); +//returns true if the bot is in observer mode +qboolean BotIsObserver(bot_state_t *bs); +//returns true if the bot is in the intermission +qboolean BotIntermission(bot_state_t *bs); +//returns true if the bot is in lava or slime +qboolean BotInLavaOrSlime(bot_state_t *bs); +//returns true if the entity is dead +qboolean EntityIsDead(aas_entityinfo_t *entinfo); +//returns true if the entity is invisible +qboolean EntityIsInvisible(aas_entityinfo_t *entinfo); +//returns true if the entity is shooting +qboolean EntityIsShooting(aas_entityinfo_t *entinfo); +#ifdef MISSIONPACK +//returns true if this entity has the kamikaze +qboolean EntityHasKamikaze(aas_entityinfo_t *entinfo); +#endif +// set a user info key/value pair +void BotSetUserInfo(bot_state_t *bs, char *key, char *value); +// set the team status (offense, defense etc.) +void BotSetTeamStatus(bot_state_t *bs); +//returns the name of the client +char *ClientName(int client, char *name, int size); +//returns an simplyfied client name +char *EasyClientName(int client, char *name, int size); +//returns the skin used by the client +char *ClientSkin(int client, char *skin, int size); +// returns the appropriate synonym context for the current game type and situation +int BotSynonymContext(bot_state_t *bs); +// set last ordered task +int BotSetLastOrderedTask(bot_state_t *bs); +// selection of goals for teamplay +void BotTeamGoals(bot_state_t *bs, int retreat); +//returns the aggression of the bot in the range [0, 100] +float BotAggression(bot_state_t *bs); +//returns how bad the bot feels +float BotFeelingBad(bot_state_t *bs); +//returns true if the bot wants to retreat +int BotWantsToRetreat(bot_state_t *bs); +//returns true if the bot wants to chase +int BotWantsToChase(bot_state_t *bs); +//returns true if the bot wants to help +int BotWantsToHelp(bot_state_t *bs); +//returns true if the bot can and wants to rocketjump +int BotCanAndWantsToRocketJump(bot_state_t *bs); +// returns true if the bot has a persistant powerup and a weapon +int BotHasPersistantPowerupAndWeapon(bot_state_t *bs); +//returns true if the bot wants to and goes camping +int BotWantsToCamp(bot_state_t *bs); +//the bot will perform attack movements +bot_moveresult_t BotAttackMove(bot_state_t *bs, int tfl); +//returns true if the bot and the entity are in the same team +int BotSameTeam(bot_state_t *bs, int entnum); +//returns true if teamplay is on +int TeamPlayIsOn(void); +// returns the client number of the team mate flag carrier (-1 if none) +int BotTeamFlagCarrier(bot_state_t *bs); +//returns visible team mate flag carrier if available +int BotTeamFlagCarrierVisible(bot_state_t *bs); +//returns visible enemy flag carrier if available +int BotEnemyFlagCarrierVisible(bot_state_t *bs); +//get the number of visible teammates and enemies +void BotVisibleTeamMatesAndEnemies(bot_state_t *bs, int *teammates, int *enemies, float range); +//returns true if within the field of vision for the given angles +qboolean InFieldOfVision(vec3_t viewangles, float fov, vec3_t angles); +//returns true and sets the .enemy field when an enemy is found +int BotFindEnemy(bot_state_t *bs, int curenemy); +//returns a roam goal +void BotRoamGoal(bot_state_t *bs, vec3_t goal); +//returns entity visibility in the range [0, 1] +float BotEntityVisible(int viewer, vec3_t eye, vec3_t viewangles, float fov, int ent); +//the bot will aim at the current enemy +void BotAimAtEnemy(bot_state_t *bs); +//check if the bot should attack +void BotCheckAttack(bot_state_t *bs); +//AI when the bot is blocked +void BotAIBlocked(bot_state_t *bs, bot_moveresult_t *moveresult, int activate); +//AI to predict obstacles +int BotAIPredictObstacles(bot_state_t *bs, bot_goal_t *goal); +//enable or disable the areas the blocking entity is in +void BotEnableActivateGoalAreas(bot_activategoal_t *activategoal, int enable); +//pop an activate goal from the stack +int BotPopFromActivateGoalStack(bot_state_t *bs); +//clear the activate goal stack +void BotClearActivateGoalStack(bot_state_t *bs); +//returns the team the bot is in +int BotTeam(bot_state_t *bs); +//retuns the opposite team of the bot +int BotOppositeTeam(bot_state_t *bs); +//returns the flag the bot is carrying (CTFFLAG_?) +int BotCTFCarryingFlag(bot_state_t *bs); +//remember the last ordered task +void BotRememberLastOrderedTask(bot_state_t *bs); +//set ctf goals (defend base, get enemy flag) during seek +void BotCTFSeekGoals(bot_state_t *bs); +//set ctf goals (defend base, get enemy flag) during retreat +void BotCTFRetreatGoals(bot_state_t *bs); +// +#ifdef MISSIONPACK +int Bot1FCTFCarryingFlag(bot_state_t *bs); +int BotHarvesterCarryingCubes(bot_state_t *bs); +void Bot1FCTFSeekGoals(bot_state_t *bs); +void Bot1FCTFRetreatGoals(bot_state_t *bs); +void BotObeliskSeekGoals(bot_state_t *bs); +void BotObeliskRetreatGoals(bot_state_t *bs); +void BotGoHarvest(bot_state_t *bs); +void BotHarvesterSeekGoals(bot_state_t *bs); +void BotHarvesterRetreatGoals(bot_state_t *bs); +int BotTeamCubeCarrierVisible(bot_state_t *bs); +int BotEnemyCubeCarrierVisible(bot_state_t *bs); +#endif +//get a random alternate route goal towards the given base +int BotGetAlternateRouteGoal(bot_state_t *bs, int base); +//returns either the alternate route goal or the given goal +bot_goal_t *BotAlternateRoute(bot_state_t *bs, bot_goal_t *goal); +//create a new waypoint +bot_waypoint_t *BotCreateWayPoint(char *name, vec3_t origin, int areanum); +//find a waypoint with the given name +bot_waypoint_t *BotFindWayPoint(bot_waypoint_t *waypoints, char *name); +//strstr but case insensitive +char *stristr(char *str, char *charset); +//returns the number of the client with the given name +int ClientFromName(char *name); +int ClientOnSameTeamFromName(bot_state_t *bs, char *name); +// +int BotPointAreaNum(vec3_t origin); +// +void BotMapScripts(bot_state_t *bs); + +//ctf flags +#define CTF_FLAG_NONE 0 +#define CTF_FLAG_RED 1 +#define CTF_FLAG_BLUE 2 +//CTF skins +#define CTF_SKIN_REDTEAM "red" +#define CTF_SKIN_BLUETEAM "blue" + +extern int gametype; //game type +extern int maxclients; //maximum number of clients + +extern vmCvar_t bot_grapple; +extern vmCvar_t bot_rocketjump; +extern vmCvar_t bot_fastchat; +extern vmCvar_t bot_nochat; +extern vmCvar_t bot_testrchat; +extern vmCvar_t bot_challenge; + +extern bot_goal_t ctf_redflag; +extern bot_goal_t ctf_blueflag; +#ifdef MISSIONPACK +extern bot_goal_t ctf_neutralflag; +extern bot_goal_t redobelisk; +extern bot_goal_t blueobelisk; +extern bot_goal_t neutralobelisk; +#endif diff --git a/reaction/engine/code/game/ai_main.c b/reaction/engine/code/game/ai_main.c new file mode 100644 index 00000000..fb37c283 --- /dev/null +++ b/reaction/engine/code/game/ai_main.c @@ -0,0 +1,1698 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: ai_main.c + * + * desc: Quake3 bot AI + * + * $Archive: /MissionPack/code/game/ai_main.c $ + * + *****************************************************************************/ + + +#include "g_local.h" +#include "../qcommon/q_shared.h" +#include "../botlib/botlib.h" //bot lib interface +#include "../botlib/be_aas.h" +#include "../botlib/be_ea.h" +#include "../botlib/be_ai_char.h" +#include "../botlib/be_ai_chat.h" +#include "../botlib/be_ai_gen.h" +#include "../botlib/be_ai_goal.h" +#include "../botlib/be_ai_move.h" +#include "../botlib/be_ai_weap.h" +// +#include "ai_main.h" +#include "ai_dmq3.h" +#include "ai_chat.h" +#include "ai_cmd.h" +#include "ai_dmnet.h" +#include "ai_vcmd.h" + +// +#include "chars.h" +#include "inv.h" +#include "syn.h" + +#ifndef MAX_PATH +#define MAX_PATH 144 +#endif + + +//bot states +bot_state_t *botstates[MAX_CLIENTS]; +//number of bots +int numbots; +//floating point time +float floattime; +//time to do a regular update +float regularupdate_time; +// +int bot_interbreed; +int bot_interbreedmatchcount; +// +vmCvar_t bot_thinktime; +vmCvar_t bot_memorydump; +vmCvar_t bot_saveroutingcache; +vmCvar_t bot_pause; +vmCvar_t bot_report; +vmCvar_t bot_testsolid; +vmCvar_t bot_testclusters; +vmCvar_t bot_developer; +vmCvar_t bot_interbreedchar; +vmCvar_t bot_interbreedbots; +vmCvar_t bot_interbreedcycle; +vmCvar_t bot_interbreedwrite; + + +void ExitLevel( void ); + + +/* +================== +BotAI_Print +================== +*/ +void QDECL BotAI_Print(int type, char *fmt, ...) { + char str[2048]; + va_list ap; + + va_start(ap, fmt); + Q_vsnprintf(str, sizeof(str), fmt, ap); + va_end(ap); + + switch(type) { + case PRT_MESSAGE: { + G_Printf("%s", str); + break; + } + case PRT_WARNING: { + G_Printf( S_COLOR_YELLOW "Warning: %s", str ); + break; + } + case PRT_ERROR: { + G_Printf( S_COLOR_RED "Error: %s", str ); + break; + } + case PRT_FATAL: { + G_Printf( S_COLOR_RED "Fatal: %s", str ); + break; + } + case PRT_EXIT: { + G_Error( S_COLOR_RED "Exit: %s", str ); + break; + } + default: { + G_Printf( "unknown print type\n" ); + break; + } + } +} + + +/* +================== +BotAI_Trace +================== +*/ +void BotAI_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) { + trace_t trace; + + trap_Trace(&trace, start, mins, maxs, end, passent, contentmask); + //copy the trace information + bsptrace->allsolid = trace.allsolid; + bsptrace->startsolid = trace.startsolid; + bsptrace->fraction = trace.fraction; + VectorCopy(trace.endpos, bsptrace->endpos); + bsptrace->plane.dist = trace.plane.dist; + VectorCopy(trace.plane.normal, bsptrace->plane.normal); + bsptrace->plane.signbits = trace.plane.signbits; + bsptrace->plane.type = trace.plane.type; + bsptrace->surface.value = trace.surfaceFlags; + bsptrace->ent = trace.entityNum; + bsptrace->exp_dist = 0; + bsptrace->sidenum = 0; + bsptrace->contents = 0; +} + +/* +================== +BotAI_GetClientState +================== +*/ +int BotAI_GetClientState( int clientNum, playerState_t *state ) { + gentity_t *ent; + + ent = &g_entities[clientNum]; + if ( !ent->inuse ) { + return qfalse; + } + if ( !ent->client ) { + return qfalse; + } + + memcpy( state, &ent->client->ps, sizeof(playerState_t) ); + return qtrue; +} + +/* +================== +BotAI_GetEntityState +================== +*/ +int BotAI_GetEntityState( int entityNum, entityState_t *state ) { + gentity_t *ent; + + ent = &g_entities[entityNum]; + memset( state, 0, sizeof(entityState_t) ); + if (!ent->inuse) return qfalse; + if (!ent->r.linked) return qfalse; + if (ent->r.svFlags & SVF_NOCLIENT) return qfalse; + memcpy( state, &ent->s, sizeof(entityState_t) ); + return qtrue; +} + +/* +================== +BotAI_GetSnapshotEntity +================== +*/ +int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state ) { + int entNum; + + entNum = trap_BotGetSnapshotEntity( clientNum, sequence ); + if ( entNum == -1 ) { + memset(state, 0, sizeof(entityState_t)); + return -1; + } + + BotAI_GetEntityState( entNum, state ); + + return sequence + 1; +} + +/* +================== +BotAI_BotInitialChat +================== +*/ +void QDECL BotAI_BotInitialChat( bot_state_t *bs, char *type, ... ) { + int i, mcontext; + va_list ap; + char *p; + char *vars[MAX_MATCHVARIABLES]; + + memset(vars, 0, sizeof(vars)); + va_start(ap, type); + p = va_arg(ap, char *); + for (i = 0; i < MAX_MATCHVARIABLES; i++) { + if( !p ) { + break; + } + vars[i] = p; + p = va_arg(ap, char *); + } + va_end(ap); + + mcontext = BotSynonymContext(bs); + + trap_BotInitialChat( bs->cs, type, mcontext, vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7] ); +} + + +/* +================== +BotTestAAS +================== +*/ +void BotTestAAS(vec3_t origin) { + int areanum; + aas_areainfo_t info; + + trap_Cvar_Update(&bot_testsolid); + trap_Cvar_Update(&bot_testclusters); + if (bot_testsolid.integer) { + if (!trap_AAS_Initialized()) return; + areanum = BotPointAreaNum(origin); + if (areanum) BotAI_Print(PRT_MESSAGE, "\remtpy area"); + else BotAI_Print(PRT_MESSAGE, "\r^1SOLID area"); + } + else if (bot_testclusters.integer) { + if (!trap_AAS_Initialized()) return; + areanum = BotPointAreaNum(origin); + if (!areanum) + BotAI_Print(PRT_MESSAGE, "\r^1Solid! "); + else { + trap_AAS_AreaInfo(areanum, &info); + BotAI_Print(PRT_MESSAGE, "\rarea %d, cluster %d ", areanum, info.cluster); + } + } +} + +/* +================== +BotReportStatus +================== +*/ +void BotReportStatus(bot_state_t *bs) { + char goalname[MAX_MESSAGE_SIZE]; + char netname[MAX_MESSAGE_SIZE]; + char *leader, flagstatus[32]; + // + ClientName(bs->client, netname, sizeof(netname)); + if (Q_stricmp(netname, bs->teamleader) == 0) leader = "L"; + else leader = " "; + + strcpy(flagstatus, " "); + if (gametype == GT_CTF) { + if (BotCTFCarryingFlag(bs)) { + if (BotTeam(bs) == TEAM_RED) strcpy(flagstatus, S_COLOR_RED"F "); + else strcpy(flagstatus, S_COLOR_BLUE"F "); + } + } +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF) { + if (Bot1FCTFCarryingFlag(bs)) { + if (BotTeam(bs) == TEAM_RED) strcpy(flagstatus, S_COLOR_RED"F "); + else strcpy(flagstatus, S_COLOR_BLUE"F "); + } + } + else if (gametype == GT_HARVESTER) { + if (BotHarvesterCarryingCubes(bs)) { + if (BotTeam(bs) == TEAM_RED) Com_sprintf(flagstatus, sizeof(flagstatus), S_COLOR_RED"%2d", bs->inventory[INVENTORY_REDCUBE]); + else Com_sprintf(flagstatus, sizeof(flagstatus), S_COLOR_BLUE"%2d", bs->inventory[INVENTORY_BLUECUBE]); + } + } +#endif + + switch(bs->ltgtype) { + case LTG_TEAMHELP: + { + EasyClientName(bs->teammate, goalname, sizeof(goalname)); + BotAI_Print(PRT_MESSAGE, "%-20s%s%s: helping %s\n", netname, leader, flagstatus, goalname); + break; + } + case LTG_TEAMACCOMPANY: + { + EasyClientName(bs->teammate, goalname, sizeof(goalname)); + BotAI_Print(PRT_MESSAGE, "%-20s%s%s: accompanying %s\n", netname, leader, flagstatus, goalname); + break; + } + case LTG_DEFENDKEYAREA: + { + trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); + BotAI_Print(PRT_MESSAGE, "%-20s%s%s: defending %s\n", netname, leader, flagstatus, goalname); + break; + } + case LTG_GETITEM: + { + trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); + BotAI_Print(PRT_MESSAGE, "%-20s%s%s: getting item %s\n", netname, leader, flagstatus, goalname); + break; + } + case LTG_KILL: + { + ClientName(bs->teamgoal.entitynum, goalname, sizeof(goalname)); + BotAI_Print(PRT_MESSAGE, "%-20s%s%s: killing %s\n", netname, leader, flagstatus, goalname); + break; + } + case LTG_CAMP: + case LTG_CAMPORDER: + { + BotAI_Print(PRT_MESSAGE, "%-20s%s%s: camping\n", netname, leader, flagstatus); + break; + } + case LTG_PATROL: + { + BotAI_Print(PRT_MESSAGE, "%-20s%s%s: patrolling\n", netname, leader, flagstatus); + break; + } + case LTG_GETFLAG: + { + BotAI_Print(PRT_MESSAGE, "%-20s%s%s: capturing flag\n", netname, leader, flagstatus); + break; + } + case LTG_RUSHBASE: + { + BotAI_Print(PRT_MESSAGE, "%-20s%s%s: rushing base\n", netname, leader, flagstatus); + break; + } + case LTG_RETURNFLAG: + { + BotAI_Print(PRT_MESSAGE, "%-20s%s%s: returning flag\n", netname, leader, flagstatus); + break; + } + case LTG_ATTACKENEMYBASE: + { + BotAI_Print(PRT_MESSAGE, "%-20s%s%s: attacking the enemy base\n", netname, leader, flagstatus); + break; + } + case LTG_HARVEST: + { + BotAI_Print(PRT_MESSAGE, "%-20s%s%s: harvesting\n", netname, leader, flagstatus); + break; + } + default: + { + BotAI_Print(PRT_MESSAGE, "%-20s%s%s: roaming\n", netname, leader, flagstatus); + break; + } + } +} + +/* +================== +BotTeamplayReport +================== +*/ +void BotTeamplayReport(void) { + int i; + char buf[MAX_INFO_STRING]; + + BotAI_Print(PRT_MESSAGE, S_COLOR_RED"RED\n"); + for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + // + if ( !botstates[i] || !botstates[i]->inuse ) 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_RED) { + BotReportStatus(botstates[i]); + } + } + BotAI_Print(PRT_MESSAGE, S_COLOR_BLUE"BLUE\n"); + for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + // + if ( !botstates[i] || !botstates[i]->inuse ) 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_BLUE) { + BotReportStatus(botstates[i]); + } + } +} + +/* +================== +BotSetInfoConfigString +================== +*/ +void BotSetInfoConfigString(bot_state_t *bs) { + char goalname[MAX_MESSAGE_SIZE]; + char netname[MAX_MESSAGE_SIZE]; + char action[MAX_MESSAGE_SIZE]; + char *leader, carrying[32], *cs; + bot_goal_t goal; + // + ClientName(bs->client, netname, sizeof(netname)); + if (Q_stricmp(netname, bs->teamleader) == 0) leader = "L"; + else leader = " "; + + strcpy(carrying, " "); + if (gametype == GT_CTF) { + if (BotCTFCarryingFlag(bs)) { + strcpy(carrying, "F "); + } + } +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF) { + if (Bot1FCTFCarryingFlag(bs)) { + strcpy(carrying, "F "); + } + } + else if (gametype == GT_HARVESTER) { + if (BotHarvesterCarryingCubes(bs)) { + if (BotTeam(bs) == TEAM_RED) Com_sprintf(carrying, sizeof(carrying), "%2d", bs->inventory[INVENTORY_REDCUBE]); + else Com_sprintf(carrying, sizeof(carrying), "%2d", bs->inventory[INVENTORY_BLUECUBE]); + } + } +#endif + + switch(bs->ltgtype) { + case LTG_TEAMHELP: + { + EasyClientName(bs->teammate, goalname, sizeof(goalname)); + Com_sprintf(action, sizeof(action), "helping %s", goalname); + break; + } + case LTG_TEAMACCOMPANY: + { + EasyClientName(bs->teammate, goalname, sizeof(goalname)); + Com_sprintf(action, sizeof(action), "accompanying %s", goalname); + break; + } + case LTG_DEFENDKEYAREA: + { + trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); + Com_sprintf(action, sizeof(action), "defending %s", goalname); + break; + } + case LTG_GETITEM: + { + trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); + Com_sprintf(action, sizeof(action), "getting item %s", goalname); + break; + } + case LTG_KILL: + { + ClientName(bs->teamgoal.entitynum, goalname, sizeof(goalname)); + Com_sprintf(action, sizeof(action), "killing %s", goalname); + break; + } + case LTG_CAMP: + case LTG_CAMPORDER: + { + Com_sprintf(action, sizeof(action), "camping"); + break; + } + case LTG_PATROL: + { + Com_sprintf(action, sizeof(action), "patrolling"); + break; + } + case LTG_GETFLAG: + { + Com_sprintf(action, sizeof(action), "capturing flag"); + break; + } + case LTG_RUSHBASE: + { + Com_sprintf(action, sizeof(action), "rushing base"); + break; + } + case LTG_RETURNFLAG: + { + Com_sprintf(action, sizeof(action), "returning flag"); + break; + } + case LTG_ATTACKENEMYBASE: + { + Com_sprintf(action, sizeof(action), "attacking the enemy base"); + break; + } + case LTG_HARVEST: + { + Com_sprintf(action, sizeof(action), "harvesting"); + break; + } + default: + { + trap_BotGetTopGoal(bs->gs, &goal); + trap_BotGoalName(goal.number, goalname, sizeof(goalname)); + Com_sprintf(action, sizeof(action), "roaming %s", goalname); + break; + } + } + cs = va("l\\%s\\c\\%s\\a\\%s", + leader, + carrying, + action); + trap_SetConfigstring (CS_BOTINFO + bs->client, cs); +} + +/* +============== +BotUpdateInfoConfigStrings +============== +*/ +void BotUpdateInfoConfigStrings(void) { + int i; + char buf[MAX_INFO_STRING]; + + for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + // + if ( !botstates[i] || !botstates[i]->inuse ) + 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; + BotSetInfoConfigString(botstates[i]); + } +} + +/* +============== +BotInterbreedBots +============== +*/ +void BotInterbreedBots(void) { + float ranks[MAX_CLIENTS]; + int parent1, parent2, child; + int i; + + // get rankings for all the bots + for (i = 0; i < MAX_CLIENTS; i++) { + if ( botstates[i] && botstates[i]->inuse ) { + ranks[i] = botstates[i]->num_kills * 2 - botstates[i]->num_deaths; + } + else { + ranks[i] = -1; + } + } + + if (trap_GeneticParentsAndChildSelection(MAX_CLIENTS, ranks, &parent1, &parent2, &child)) { + trap_BotInterbreedGoalFuzzyLogic(botstates[parent1]->gs, botstates[parent2]->gs, botstates[child]->gs); + trap_BotMutateGoalFuzzyLogic(botstates[child]->gs, 1); + } + // reset the kills and deaths + for (i = 0; i < MAX_CLIENTS; i++) { + if (botstates[i] && botstates[i]->inuse) { + botstates[i]->num_kills = 0; + botstates[i]->num_deaths = 0; + } + } +} + +/* +============== +BotWriteInterbreeded +============== +*/ +void BotWriteInterbreeded(char *filename) { + float rank, bestrank; + int i, bestbot; + + bestrank = 0; + bestbot = -1; + // get the best bot + for (i = 0; i < MAX_CLIENTS; i++) { + if ( botstates[i] && botstates[i]->inuse ) { + rank = botstates[i]->num_kills * 2 - botstates[i]->num_deaths; + } + else { + rank = -1; + } + if (rank > bestrank) { + bestrank = rank; + bestbot = i; + } + } + if (bestbot >= 0) { + //write out the new goal fuzzy logic + trap_BotSaveGoalFuzzyLogic(botstates[bestbot]->gs, filename); + } +} + +/* +============== +BotInterbreedEndMatch + +add link back into ExitLevel? +============== +*/ +void BotInterbreedEndMatch(void) { + + if (!bot_interbreed) return; + bot_interbreedmatchcount++; + if (bot_interbreedmatchcount >= bot_interbreedcycle.integer) { + bot_interbreedmatchcount = 0; + // + trap_Cvar_Update(&bot_interbreedwrite); + if (strlen(bot_interbreedwrite.string)) { + BotWriteInterbreeded(bot_interbreedwrite.string); + trap_Cvar_Set("bot_interbreedwrite", ""); + } + BotInterbreedBots(); + } +} + +/* +============== +BotInterbreeding +============== +*/ +void BotInterbreeding(void) { + int i; + + trap_Cvar_Update(&bot_interbreedchar); + if (!strlen(bot_interbreedchar.string)) return; + //make sure we are in tournament mode + if (gametype != GT_TOURNAMENT) { + trap_Cvar_Set("g_gametype", va("%d", GT_TOURNAMENT)); + ExitLevel(); + return; + } + //shutdown all the bots + for (i = 0; i < MAX_CLIENTS; i++) { + if (botstates[i] && botstates[i]->inuse) { + BotAIShutdownClient(botstates[i]->client, qfalse); + } + } + //make sure all item weight configs are reloaded and Not shared + trap_BotLibVarSet("bot_reloadcharacters", "1"); + //add a number of bots using the desired bot character + for (i = 0; i < bot_interbreedbots.integer; i++) { + trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s 4 free %i %s%d\n", + bot_interbreedchar.string, i * 50, bot_interbreedchar.string, i) ); + } + // + trap_Cvar_Set("bot_interbreedchar", ""); + bot_interbreed = qtrue; +} + +/* +============== +BotEntityInfo +============== +*/ +void BotEntityInfo(int entnum, aas_entityinfo_t *info) { + trap_AAS_EntityInfo(entnum, info); +} + +/* +============== +NumBots +============== +*/ +int NumBots(void) { + return numbots; +} + +/* +============== +BotTeamLeader +============== +*/ +int BotTeamLeader(bot_state_t *bs) { + int leader; + + leader = ClientFromName(bs->teamleader); + if (leader < 0) return qfalse; + if (!botstates[leader] || !botstates[leader]->inuse) return qfalse; + return qtrue; +} + +/* +============== +AngleDifference +============== +*/ +float AngleDifference(float ang1, float ang2) { + float diff; + + diff = ang1 - ang2; + if (ang1 > ang2) { + if (diff > 180.0) diff -= 360.0; + } + else { + if (diff < -180.0) diff += 360.0; + } + return diff; +} + +/* +============== +BotChangeViewAngle +============== +*/ +float BotChangeViewAngle(float angle, float ideal_angle, float speed) { + float move; + + angle = AngleMod(angle); + ideal_angle = AngleMod(ideal_angle); + if (angle == ideal_angle) return angle; + move = ideal_angle - angle; + if (ideal_angle > angle) { + if (move > 180.0) move -= 360.0; + } + else { + if (move < -180.0) move += 360.0; + } + if (move > 0) { + if (move > speed) move = speed; + } + else { + if (move < -speed) move = -speed; + } + return AngleMod(angle + move); +} + +/* +============== +BotChangeViewAngles +============== +*/ +void BotChangeViewAngles(bot_state_t *bs, float thinktime) { + float diff, factor, maxchange, anglespeed, disired_speed; + int i; + + if (bs->ideal_viewangles[PITCH] > 180) bs->ideal_viewangles[PITCH] -= 360; + // + if (bs->enemy >= 0) { + factor = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_FACTOR, 0.01f, 1); + maxchange = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_MAXCHANGE, 1, 1800); + } + else { + factor = 0.05f; + maxchange = 360; + } + if (maxchange < 240) maxchange = 240; + maxchange *= thinktime; + for (i = 0; i < 2; i++) { + // + if (bot_challenge.integer) { + //smooth slowdown view model + diff = abs(AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i])); + anglespeed = diff * factor; + if (anglespeed > maxchange) anglespeed = maxchange; + bs->viewangles[i] = BotChangeViewAngle(bs->viewangles[i], + bs->ideal_viewangles[i], anglespeed); + } + else { + //over reaction view model + bs->viewangles[i] = AngleMod(bs->viewangles[i]); + bs->ideal_viewangles[i] = AngleMod(bs->ideal_viewangles[i]); + diff = AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]); + disired_speed = diff * factor; + bs->viewanglespeed[i] += (bs->viewanglespeed[i] - disired_speed); + if (bs->viewanglespeed[i] > 180) bs->viewanglespeed[i] = maxchange; + if (bs->viewanglespeed[i] < -180) bs->viewanglespeed[i] = -maxchange; + anglespeed = bs->viewanglespeed[i]; + if (anglespeed > maxchange) anglespeed = maxchange; + if (anglespeed < -maxchange) anglespeed = -maxchange; + bs->viewangles[i] += anglespeed; + bs->viewangles[i] = AngleMod(bs->viewangles[i]); + //demping + bs->viewanglespeed[i] *= 0.45 * (1 - factor); + } + //BotAI_Print(PRT_MESSAGE, "ideal_angles %f %f\n", bs->ideal_viewangles[0], bs->ideal_viewangles[1], bs->ideal_viewangles[2]);` + //bs->viewangles[i] = bs->ideal_viewangles[i]; + } + //bs->viewangles[PITCH] = 0; + if (bs->viewangles[PITCH] > 180) bs->viewangles[PITCH] -= 360; + //elementary action: view + trap_EA_View(bs->client, bs->viewangles); +} + +/* +============== +BotInputToUserCommand +============== +*/ +void BotInputToUserCommand(bot_input_t *bi, usercmd_t *ucmd, int delta_angles[3], int time) { + vec3_t angles, forward, right; + short temp; + int j; + + //clear the whole structure + memset(ucmd, 0, sizeof(usercmd_t)); + // + //Com_Printf("dir = %f %f %f speed = %f\n", bi->dir[0], bi->dir[1], bi->dir[2], bi->speed); + //the duration for the user command in milli seconds + ucmd->serverTime = time; + // + if (bi->actionflags & ACTION_DELAYEDJUMP) { + bi->actionflags |= ACTION_JUMP; + bi->actionflags &= ~ACTION_DELAYEDJUMP; + } + //set the buttons + if (bi->actionflags & ACTION_RESPAWN) ucmd->buttons = BUTTON_ATTACK; + if (bi->actionflags & ACTION_ATTACK) ucmd->buttons |= BUTTON_ATTACK; + if (bi->actionflags & ACTION_TALK) ucmd->buttons |= BUTTON_TALK; + if (bi->actionflags & ACTION_GESTURE) ucmd->buttons |= BUTTON_GESTURE; + if (bi->actionflags & ACTION_USE) ucmd->buttons |= BUTTON_USE_HOLDABLE; + if (bi->actionflags & ACTION_WALK) ucmd->buttons |= BUTTON_WALKING; + if (bi->actionflags & ACTION_AFFIRMATIVE) ucmd->buttons |= BUTTON_AFFIRMATIVE; + if (bi->actionflags & ACTION_NEGATIVE) ucmd->buttons |= BUTTON_NEGATIVE; + if (bi->actionflags & ACTION_GETFLAG) ucmd->buttons |= BUTTON_GETFLAG; + if (bi->actionflags & ACTION_GUARDBASE) ucmd->buttons |= BUTTON_GUARDBASE; + if (bi->actionflags & ACTION_PATROL) ucmd->buttons |= BUTTON_PATROL; + if (bi->actionflags & ACTION_FOLLOWME) ucmd->buttons |= BUTTON_FOLLOWME; + // + ucmd->weapon = bi->weapon; + //set the view angles + //NOTE: the ucmd->angles are the angles WITHOUT the delta angles + ucmd->angles[PITCH] = ANGLE2SHORT(bi->viewangles[PITCH]); + ucmd->angles[YAW] = ANGLE2SHORT(bi->viewangles[YAW]); + ucmd->angles[ROLL] = ANGLE2SHORT(bi->viewangles[ROLL]); + //subtract the delta angles + for (j = 0; j < 3; j++) { + temp = ucmd->angles[j] - delta_angles[j]; + /*NOTE: disabled because temp should be mod first + if ( j == PITCH ) { + // don't let the player look up or down more than 90 degrees + if ( temp > 16000 ) temp = 16000; + else if ( temp < -16000 ) temp = -16000; + } + */ + ucmd->angles[j] = temp; + } + //NOTE: movement is relative to the REAL view angles + //get the horizontal forward and right vector + //get the pitch in the range [-180, 180] + if (bi->dir[2]) angles[PITCH] = bi->viewangles[PITCH]; + else angles[PITCH] = 0; + angles[YAW] = bi->viewangles[YAW]; + angles[ROLL] = 0; + AngleVectors(angles, forward, right, NULL); + //bot input speed is in the range [0, 400] + bi->speed = bi->speed * 127 / 400; + //set the view independent movement + ucmd->forwardmove = DotProduct(forward, bi->dir) * bi->speed; + ucmd->rightmove = DotProduct(right, bi->dir) * bi->speed; + ucmd->upmove = abs(forward[2]) * bi->dir[2] * bi->speed; + //normal keyboard movement + if (bi->actionflags & ACTION_MOVEFORWARD) ucmd->forwardmove += 127; + if (bi->actionflags & ACTION_MOVEBACK) ucmd->forwardmove -= 127; + if (bi->actionflags & ACTION_MOVELEFT) ucmd->rightmove -= 127; + if (bi->actionflags & ACTION_MOVERIGHT) ucmd->rightmove += 127; + //jump/moveup + if (bi->actionflags & ACTION_JUMP) ucmd->upmove += 127; + //crouch/movedown + if (bi->actionflags & ACTION_CROUCH) ucmd->upmove -= 127; + // + //Com_Printf("forward = %d right = %d up = %d\n", ucmd.forwardmove, ucmd.rightmove, ucmd.upmove); + //Com_Printf("ucmd->serverTime = %d\n", ucmd->serverTime); +} + +/* +============== +BotUpdateInput +============== +*/ +void BotUpdateInput(bot_state_t *bs, int time, int elapsed_time) { + bot_input_t bi; + int j; + + //add the delta angles to the bot's current view angles + for (j = 0; j < 3; j++) { + bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j])); + } + //change the bot view angles + BotChangeViewAngles(bs, (float) elapsed_time / 1000); + //retrieve the bot input + trap_EA_GetInput(bs->client, (float) time / 1000, &bi); + //respawn hack + if (bi.actionflags & ACTION_RESPAWN) { + if (bs->lastucmd.buttons & BUTTON_ATTACK) bi.actionflags &= ~(ACTION_RESPAWN|ACTION_ATTACK); + } + //convert the bot input to a usercmd + BotInputToUserCommand(&bi, &bs->lastucmd, bs->cur_ps.delta_angles, time); + //subtract the delta angles + for (j = 0; j < 3; j++) { + bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j])); + } +} + +/* +============== +BotAIRegularUpdate +============== +*/ +void BotAIRegularUpdate(void) { + if (regularupdate_time < FloatTime()) { + trap_BotUpdateEntityItems(); + regularupdate_time = FloatTime() + 0.3; + } +} + +/* +============== +RemoveColorEscapeSequences +============== +*/ +void RemoveColorEscapeSequences( char *text ) { + int i, l; + + l = 0; + for ( i = 0; text[i]; i++ ) { + if (Q_IsColorString(&text[i])) { + i++; + continue; + } + if (text[i] > 0x7E) + continue; + text[l++] = text[i]; + } + text[l] = '\0'; +} + +/* +============== +BotAI +============== +*/ +int BotAI(int client, float thinktime) { + bot_state_t *bs; + char buf[1024], *args; + int j; + + trap_EA_ResetInput(client); + // + bs = botstates[client]; + if (!bs || !bs->inuse) { + BotAI_Print(PRT_FATAL, "BotAI: client %d is not setup\n", client); + return qfalse; + } + + //retrieve the current client state + BotAI_GetClientState( client, &bs->cur_ps ); + + //retrieve any waiting server commands + while( trap_BotGetServerCommand(client, buf, sizeof(buf)) ) { + //have buf point to the command and args to the command arguments + args = strchr( buf, ' '); + if (!args) continue; + *args++ = '\0'; + + //remove color espace sequences from the arguments + RemoveColorEscapeSequences( args ); + + if (!Q_stricmp(buf, "cp ")) + { /*CenterPrintf*/ } + else if (!Q_stricmp(buf, "cs")) + { /*ConfigStringModified*/ } + else if (!Q_stricmp(buf, "print")) { + //remove first and last quote from the chat message + memmove(args, args+1, strlen(args)); + args[strlen(args)-1] = '\0'; + trap_BotQueueConsoleMessage(bs->cs, CMS_NORMAL, args); + } + else if (!Q_stricmp(buf, "chat")) { + //remove first and last quote from the chat message + memmove(args, args+1, strlen(args)); + args[strlen(args)-1] = '\0'; + trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args); + } + else if (!Q_stricmp(buf, "tchat")) { + //remove first and last quote from the chat message + memmove(args, args+1, strlen(args)); + args[strlen(args)-1] = '\0'; + trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args); + } +#ifdef MISSIONPACK + else if (!Q_stricmp(buf, "vchat")) { + BotVoiceChatCommand(bs, SAY_ALL, args); + } + else if (!Q_stricmp(buf, "vtchat")) { + BotVoiceChatCommand(bs, SAY_TEAM, args); + } + else if (!Q_stricmp(buf, "vtell")) { + BotVoiceChatCommand(bs, SAY_TELL, args); + } +#endif + else if (!Q_stricmp(buf, "scores")) + { /*FIXME: parse scores?*/ } + else if (!Q_stricmp(buf, "clientLevelShot")) + { /*ignore*/ } + } + //add the delta angles to the bot's current view angles + for (j = 0; j < 3; j++) { + bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j])); + } + //increase the local time of the bot + bs->ltime += thinktime; + // + bs->thinktime = thinktime; + //origin of the bot + VectorCopy(bs->cur_ps.origin, bs->origin); + //eye coordinates of the bot + VectorCopy(bs->cur_ps.origin, bs->eye); + bs->eye[2] += bs->cur_ps.viewheight; + //get the area the bot is in + bs->areanum = BotPointAreaNum(bs->origin); + //the real AI + BotDeathmatchAI(bs, thinktime); + //set the weapon selection every AI frame + trap_EA_SelectWeapon(bs->client, bs->weaponnum); + //subtract the delta angles + for (j = 0; j < 3; j++) { + bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j])); + } + //everything was ok + return qtrue; +} + +/* +================== +BotScheduleBotThink +================== +*/ +void BotScheduleBotThink(void) { + int i, botnum; + + botnum = 0; + + for( i = 0; i < MAX_CLIENTS; i++ ) { + if( !botstates[i] || !botstates[i]->inuse ) { + continue; + } + //initialize the bot think residual time + botstates[i]->botthink_residual = bot_thinktime.integer * botnum / numbots; + botnum++; + } +} + +/* +============== +BotWriteSessionData +============== +*/ +void BotWriteSessionData(bot_state_t *bs) { + const char *s; + const char *var; + + s = va( + "%i %i %i %i %i %i %i %i" + " %f %f %f" + " %f %f %f" + " %f %f %f", + bs->lastgoal_decisionmaker, + bs->lastgoal_ltgtype, + bs->lastgoal_teammate, + bs->lastgoal_teamgoal.areanum, + bs->lastgoal_teamgoal.entitynum, + bs->lastgoal_teamgoal.flags, + bs->lastgoal_teamgoal.iteminfo, + bs->lastgoal_teamgoal.number, + bs->lastgoal_teamgoal.origin[0], + bs->lastgoal_teamgoal.origin[1], + bs->lastgoal_teamgoal.origin[2], + bs->lastgoal_teamgoal.mins[0], + bs->lastgoal_teamgoal.mins[1], + bs->lastgoal_teamgoal.mins[2], + bs->lastgoal_teamgoal.maxs[0], + bs->lastgoal_teamgoal.maxs[1], + bs->lastgoal_teamgoal.maxs[2] + ); + + var = va( "botsession%i", bs->client ); + + trap_Cvar_Set( var, s ); +} + +/* +============== +BotReadSessionData +============== +*/ +void BotReadSessionData(bot_state_t *bs) { + char s[MAX_STRING_CHARS]; + const char *var; + + var = va( "botsession%i", bs->client ); + trap_Cvar_VariableStringBuffer( var, s, sizeof(s) ); + + sscanf(s, + "%i %i %i %i %i %i %i %i" + " %f %f %f" + " %f %f %f" + " %f %f %f", + &bs->lastgoal_decisionmaker, + &bs->lastgoal_ltgtype, + &bs->lastgoal_teammate, + &bs->lastgoal_teamgoal.areanum, + &bs->lastgoal_teamgoal.entitynum, + &bs->lastgoal_teamgoal.flags, + &bs->lastgoal_teamgoal.iteminfo, + &bs->lastgoal_teamgoal.number, + &bs->lastgoal_teamgoal.origin[0], + &bs->lastgoal_teamgoal.origin[1], + &bs->lastgoal_teamgoal.origin[2], + &bs->lastgoal_teamgoal.mins[0], + &bs->lastgoal_teamgoal.mins[1], + &bs->lastgoal_teamgoal.mins[2], + &bs->lastgoal_teamgoal.maxs[0], + &bs->lastgoal_teamgoal.maxs[1], + &bs->lastgoal_teamgoal.maxs[2] + ); +} + +/* +============== +BotAISetupClient +============== +*/ +int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean restart) { + char filename[MAX_PATH], name[MAX_PATH], gender[MAX_PATH]; + bot_state_t *bs; + int errnum; + + if (!botstates[client]) botstates[client] = G_Alloc(sizeof(bot_state_t)); + bs = botstates[client]; + + if (bs && bs->inuse) { + BotAI_Print(PRT_FATAL, "BotAISetupClient: client %d already setup\n", client); + return qfalse; + } + + if (!trap_AAS_Initialized()) { + BotAI_Print(PRT_FATAL, "AAS not initialized\n"); + return qfalse; + } + + //load the bot character + bs->character = trap_BotLoadCharacter(settings->characterfile, settings->skill); + if (!bs->character) { + BotAI_Print(PRT_FATAL, "couldn't load skill %f from %s\n", settings->skill, settings->characterfile); + return qfalse; + } + //copy the settings + memcpy(&bs->settings, settings, sizeof(bot_settings_t)); + //allocate a goal state + bs->gs = trap_BotAllocGoalState(client); + //load the item weights + trap_Characteristic_String(bs->character, CHARACTERISTIC_ITEMWEIGHTS, filename, MAX_PATH); + errnum = trap_BotLoadItemWeights(bs->gs, filename); + if (errnum != BLERR_NOERROR) { + trap_BotFreeGoalState(bs->gs); + return qfalse; + } + //allocate a weapon state + bs->ws = trap_BotAllocWeaponState(); + //load the weapon weights + trap_Characteristic_String(bs->character, CHARACTERISTIC_WEAPONWEIGHTS, filename, MAX_PATH); + errnum = trap_BotLoadWeaponWeights(bs->ws, filename); + if (errnum != BLERR_NOERROR) { + trap_BotFreeGoalState(bs->gs); + trap_BotFreeWeaponState(bs->ws); + return qfalse; + } + //allocate a chat state + bs->cs = trap_BotAllocChatState(); + //load the chat file + trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_FILE, filename, MAX_PATH); + trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_NAME, name, MAX_PATH); + errnum = trap_BotLoadChatFile(bs->cs, filename, name); + if (errnum != BLERR_NOERROR) { + trap_BotFreeChatState(bs->cs); + trap_BotFreeGoalState(bs->gs); + trap_BotFreeWeaponState(bs->ws); + return qfalse; + } + //get the gender characteristic + trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, MAX_PATH); + //set the chat gender + if (*gender == 'f' || *gender == 'F') trap_BotSetChatGender(bs->cs, CHAT_GENDERFEMALE); + else if (*gender == 'm' || *gender == 'M') trap_BotSetChatGender(bs->cs, CHAT_GENDERMALE); + else trap_BotSetChatGender(bs->cs, CHAT_GENDERLESS); + + bs->inuse = qtrue; + bs->client = client; + bs->entitynum = client; + bs->setupcount = 4; + bs->entergame_time = FloatTime(); + bs->ms = trap_BotAllocMoveState(); + bs->walker = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_WALKER, 0, 1); + numbots++; + + if (trap_Cvar_VariableIntegerValue("bot_testichat")) { + trap_BotLibVarSet("bot_testichat", "1"); + BotChatTest(bs); + } + //NOTE: reschedule the bot thinking + BotScheduleBotThink(); + //if interbreeding start with a mutation + if (bot_interbreed) { + trap_BotMutateGoalFuzzyLogic(bs->gs, 1); + } + // if we kept the bot client + if (restart) { + BotReadSessionData(bs); + } + //bot has been setup succesfully + return qtrue; +} + +/* +============== +BotAIShutdownClient +============== +*/ +int BotAIShutdownClient(int client, qboolean restart) { + bot_state_t *bs; + + bs = botstates[client]; + if (!bs || !bs->inuse) { + //BotAI_Print(PRT_ERROR, "BotAIShutdownClient: client %d already shutdown\n", client); + return qfalse; + } + + if (restart) { + BotWriteSessionData(bs); + } + + if (BotChat_ExitGame(bs)) { + trap_BotEnterChat(bs->cs, bs->client, CHAT_ALL); + } + + trap_BotFreeMoveState(bs->ms); + //free the goal state` + trap_BotFreeGoalState(bs->gs); + //free the chat file + trap_BotFreeChatState(bs->cs); + //free the weapon weights + trap_BotFreeWeaponState(bs->ws); + //free the bot character + trap_BotFreeCharacter(bs->character); + // + BotFreeWaypoints(bs->checkpoints); + BotFreeWaypoints(bs->patrolpoints); + //clear activate goal stack + BotClearActivateGoalStack(bs); + //clear the bot state + memset(bs, 0, sizeof(bot_state_t)); + //set the inuse flag to qfalse + bs->inuse = qfalse; + //there's one bot less + numbots--; + //everything went ok + return qtrue; +} + +/* +============== +BotResetState + +called when a bot enters the intermission or observer mode and +when the level is changed +============== +*/ +void BotResetState(bot_state_t *bs) { + int client, entitynum, inuse; + int movestate, goalstate, chatstate, weaponstate; + bot_settings_t settings; + int character; + playerState_t ps; //current player state + float entergame_time; + + //save some things that should not be reset here + memcpy(&settings, &bs->settings, sizeof(bot_settings_t)); + memcpy(&ps, &bs->cur_ps, sizeof(playerState_t)); + inuse = bs->inuse; + client = bs->client; + entitynum = bs->entitynum; + character = bs->character; + movestate = bs->ms; + goalstate = bs->gs; + chatstate = bs->cs; + weaponstate = bs->ws; + entergame_time = bs->entergame_time; + //free checkpoints and patrol points + BotFreeWaypoints(bs->checkpoints); + BotFreeWaypoints(bs->patrolpoints); + //reset the whole state + memset(bs, 0, sizeof(bot_state_t)); + //copy back some state stuff that should not be reset + bs->ms = movestate; + bs->gs = goalstate; + bs->cs = chatstate; + bs->ws = weaponstate; + memcpy(&bs->cur_ps, &ps, sizeof(playerState_t)); + memcpy(&bs->settings, &settings, sizeof(bot_settings_t)); + bs->inuse = inuse; + bs->client = client; + bs->entitynum = entitynum; + bs->character = character; + bs->entergame_time = entergame_time; + //reset several states + if (bs->ms) trap_BotResetMoveState(bs->ms); + if (bs->gs) trap_BotResetGoalState(bs->gs); + if (bs->ws) trap_BotResetWeaponState(bs->ws); + if (bs->gs) trap_BotResetAvoidGoals(bs->gs); + if (bs->ms) trap_BotResetAvoidReach(bs->ms); +} + +/* +============== +BotAILoadMap +============== +*/ +int BotAILoadMap( int restart ) { + int i; + vmCvar_t mapname; + + if (!restart) { + trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM ); + trap_BotLibLoadMap( mapname.string ); + } + + for (i = 0; i < MAX_CLIENTS; i++) { + if (botstates[i] && botstates[i]->inuse) { + BotResetState( botstates[i] ); + botstates[i]->setupcount = 4; + } + } + + BotSetupDeathmatchAI(); + + return qtrue; +} + +#ifdef MISSIONPACK +void ProximityMine_Trigger( gentity_t *trigger, gentity_t *other, trace_t *trace ); +#endif + +/* +================== +BotAIStartFrame +================== +*/ +int BotAIStartFrame(int time) { + int i; + gentity_t *ent; + bot_entitystate_t state; + int elapsed_time, thinktime; + static int local_time; + static int botlib_residual; + static int lastbotthink_time; + + G_CheckBotSpawn(); + + trap_Cvar_Update(&bot_rocketjump); + trap_Cvar_Update(&bot_grapple); + trap_Cvar_Update(&bot_fastchat); + trap_Cvar_Update(&bot_nochat); + trap_Cvar_Update(&bot_testrchat); + trap_Cvar_Update(&bot_thinktime); + trap_Cvar_Update(&bot_memorydump); + trap_Cvar_Update(&bot_saveroutingcache); + trap_Cvar_Update(&bot_pause); + trap_Cvar_Update(&bot_report); + + if (bot_report.integer) { +// BotTeamplayReport(); +// trap_Cvar_Set("bot_report", "0"); + BotUpdateInfoConfigStrings(); + } + + if (bot_pause.integer) { + // execute bot user commands every frame + for( i = 0; i < MAX_CLIENTS; i++ ) { + if( !botstates[i] || !botstates[i]->inuse ) { + continue; + } + if( g_entities[i].client->pers.connected != CON_CONNECTED ) { + continue; + } + botstates[i]->lastucmd.forwardmove = 0; + botstates[i]->lastucmd.rightmove = 0; + botstates[i]->lastucmd.upmove = 0; + botstates[i]->lastucmd.buttons = 0; + botstates[i]->lastucmd.serverTime = time; + trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd); + } + return qtrue; + } + + if (bot_memorydump.integer) { + trap_BotLibVarSet("memorydump", "1"); + trap_Cvar_Set("bot_memorydump", "0"); + } + if (bot_saveroutingcache.integer) { + trap_BotLibVarSet("saveroutingcache", "1"); + trap_Cvar_Set("bot_saveroutingcache", "0"); + } + //check if bot interbreeding is activated + BotInterbreeding(); + //cap the bot think time + if (bot_thinktime.integer > 200) { + trap_Cvar_Set("bot_thinktime", "200"); + } + //if the bot think time changed we should reschedule the bots + if (bot_thinktime.integer != lastbotthink_time) { + lastbotthink_time = bot_thinktime.integer; + BotScheduleBotThink(); + } + + elapsed_time = time - local_time; + local_time = time; + + botlib_residual += elapsed_time; + + if (elapsed_time > bot_thinktime.integer) thinktime = elapsed_time; + else thinktime = bot_thinktime.integer; + + // update the bot library + if ( botlib_residual >= thinktime ) { + botlib_residual -= thinktime; + + trap_BotLibStartFrame((float) time / 1000); + + if (!trap_AAS_Initialized()) return qfalse; + + //update entities in the botlib + for (i = 0; i < MAX_GENTITIES; i++) { + ent = &g_entities[i]; + if (!ent->inuse) { + trap_BotLibUpdateEntity(i, NULL); + continue; + } + if (!ent->r.linked) { + trap_BotLibUpdateEntity(i, NULL); + continue; + } + if (ent->r.svFlags & SVF_NOCLIENT) { + trap_BotLibUpdateEntity(i, NULL); + continue; + } + // do not update missiles + if (ent->s.eType == ET_MISSILE && ent->s.weapon != WP_GRAPPLING_HOOK) { + trap_BotLibUpdateEntity(i, NULL); + continue; + } + // do not update event only entities + if (ent->s.eType > ET_EVENTS) { + trap_BotLibUpdateEntity(i, NULL); + continue; + } +#ifdef MISSIONPACK + // never link prox mine triggers + if (ent->r.contents == CONTENTS_TRIGGER) { + if (ent->touch == ProximityMine_Trigger) { + trap_BotLibUpdateEntity(i, NULL); + continue; + } + } +#endif + // + memset(&state, 0, sizeof(bot_entitystate_t)); + // + VectorCopy(ent->r.currentOrigin, state.origin); + if (i < MAX_CLIENTS) { + VectorCopy(ent->s.apos.trBase, state.angles); + } else { + VectorCopy(ent->r.currentAngles, state.angles); + } + VectorCopy(ent->s.origin2, state.old_origin); + VectorCopy(ent->r.mins, state.mins); + VectorCopy(ent->r.maxs, state.maxs); + state.type = ent->s.eType; + state.flags = ent->s.eFlags; + if (ent->r.bmodel) state.solid = SOLID_BSP; + else state.solid = SOLID_BBOX; + state.groundent = ent->s.groundEntityNum; + state.modelindex = ent->s.modelindex; + state.modelindex2 = ent->s.modelindex2; + state.frame = ent->s.frame; + state.event = ent->s.event; + state.eventParm = ent->s.eventParm; + state.powerups = ent->s.powerups; + state.legsAnim = ent->s.legsAnim; + state.torsoAnim = ent->s.torsoAnim; + state.weapon = ent->s.weapon; + // + trap_BotLibUpdateEntity(i, &state); + } + + BotAIRegularUpdate(); + } + + floattime = trap_AAS_Time(); + + // execute scheduled bot AI + for( i = 0; i < MAX_CLIENTS; i++ ) { + if( !botstates[i] || !botstates[i]->inuse ) { + continue; + } + // + botstates[i]->botthink_residual += elapsed_time; + // + if ( botstates[i]->botthink_residual >= thinktime ) { + botstates[i]->botthink_residual -= thinktime; + + if (!trap_AAS_Initialized()) return qfalse; + + if (g_entities[i].client->pers.connected == CON_CONNECTED) { + BotAI(i, (float) thinktime / 1000); + } + } + } + + + // execute bot user commands every frame + for( i = 0; i < MAX_CLIENTS; i++ ) { + if( !botstates[i] || !botstates[i]->inuse ) { + continue; + } + if( g_entities[i].client->pers.connected != CON_CONNECTED ) { + continue; + } + + BotUpdateInput(botstates[i], time, elapsed_time); + trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd); + } + + return qtrue; +} + +/* +============== +BotInitLibrary +============== +*/ +int BotInitLibrary(void) { + char buf[144]; + + //set the maxclients and maxentities library variables before calling BotSetupLibrary + trap_Cvar_VariableStringBuffer("sv_maxclients", buf, sizeof(buf)); + if (!strlen(buf)) strcpy(buf, "8"); + trap_BotLibVarSet("maxclients", buf); + Com_sprintf(buf, sizeof(buf), "%d", MAX_GENTITIES); + trap_BotLibVarSet("maxentities", buf); + //bsp checksum + trap_Cvar_VariableStringBuffer("sv_mapChecksum", buf, sizeof(buf)); + if (strlen(buf)) trap_BotLibVarSet("sv_mapChecksum", buf); + //maximum number of aas links + trap_Cvar_VariableStringBuffer("max_aaslinks", buf, sizeof(buf)); + if (strlen(buf)) trap_BotLibVarSet("max_aaslinks", buf); + //maximum number of items in a level + trap_Cvar_VariableStringBuffer("max_levelitems", buf, sizeof(buf)); + if (strlen(buf)) trap_BotLibVarSet("max_levelitems", buf); + //game type + trap_Cvar_VariableStringBuffer("g_gametype", buf, sizeof(buf)); + if (!strlen(buf)) strcpy(buf, "0"); + trap_BotLibVarSet("g_gametype", buf); + //bot developer mode and log file + trap_BotLibVarSet("bot_developer", bot_developer.string); + trap_Cvar_VariableStringBuffer("logfile", buf, sizeof(buf)); + trap_BotLibVarSet("log", buf); + //no chatting + trap_Cvar_VariableStringBuffer("bot_nochat", buf, sizeof(buf)); + if (strlen(buf)) trap_BotLibVarSet("nochat", buf); + //visualize jump pads + trap_Cvar_VariableStringBuffer("bot_visualizejumppads", buf, sizeof(buf)); + if (strlen(buf)) trap_BotLibVarSet("bot_visualizejumppads", buf); + //forced clustering calculations + trap_Cvar_VariableStringBuffer("bot_forceclustering", buf, sizeof(buf)); + if (strlen(buf)) trap_BotLibVarSet("forceclustering", buf); + //forced reachability calculations + trap_Cvar_VariableStringBuffer("bot_forcereachability", buf, sizeof(buf)); + if (strlen(buf)) trap_BotLibVarSet("forcereachability", buf); + //force writing of AAS to file + trap_Cvar_VariableStringBuffer("bot_forcewrite", buf, sizeof(buf)); + if (strlen(buf)) trap_BotLibVarSet("forcewrite", buf); + //no AAS optimization + trap_Cvar_VariableStringBuffer("bot_aasoptimize", buf, sizeof(buf)); + if (strlen(buf)) trap_BotLibVarSet("aasoptimize", buf); + // + trap_Cvar_VariableStringBuffer("bot_saveroutingcache", buf, sizeof(buf)); + if (strlen(buf)) trap_BotLibVarSet("saveroutingcache", buf); + //reload instead of cache bot character files + trap_Cvar_VariableStringBuffer("bot_reloadcharacters", buf, sizeof(buf)); + if (!strlen(buf)) strcpy(buf, "0"); + trap_BotLibVarSet("bot_reloadcharacters", buf); + //base directory + trap_Cvar_VariableStringBuffer("fs_basepath", buf, sizeof(buf)); + if (strlen(buf)) trap_BotLibVarSet("basedir", buf); + //game directory + trap_Cvar_VariableStringBuffer("fs_game", buf, sizeof(buf)); + if (strlen(buf)) trap_BotLibVarSet("gamedir", buf); + //home directory + trap_Cvar_VariableStringBuffer("fs_homepath", buf, sizeof(buf)); + if (strlen(buf)) trap_BotLibVarSet("homedir", buf); + // +#ifdef MISSIONPACK + trap_BotLibDefine("MISSIONPACK"); +#endif + //setup the bot library + return trap_BotLibSetup(); +} + +/* +============== +BotAISetup +============== +*/ +int BotAISetup( int restart ) { + int errnum; + + trap_Cvar_Register(&bot_thinktime, "bot_thinktime", "100", CVAR_CHEAT); + trap_Cvar_Register(&bot_memorydump, "bot_memorydump", "0", CVAR_CHEAT); + trap_Cvar_Register(&bot_saveroutingcache, "bot_saveroutingcache", "0", CVAR_CHEAT); + trap_Cvar_Register(&bot_pause, "bot_pause", "0", CVAR_CHEAT); + trap_Cvar_Register(&bot_report, "bot_report", "0", CVAR_CHEAT); + trap_Cvar_Register(&bot_testsolid, "bot_testsolid", "0", CVAR_CHEAT); + trap_Cvar_Register(&bot_testclusters, "bot_testclusters", "0", CVAR_CHEAT); + trap_Cvar_Register(&bot_developer, "bot_developer", "0", CVAR_CHEAT); + trap_Cvar_Register(&bot_interbreedchar, "bot_interbreedchar", "", 0); + trap_Cvar_Register(&bot_interbreedbots, "bot_interbreedbots", "10", 0); + trap_Cvar_Register(&bot_interbreedcycle, "bot_interbreedcycle", "20", 0); + trap_Cvar_Register(&bot_interbreedwrite, "bot_interbreedwrite", "", 0); + + //if the game is restarted for a tournament + if (restart) { + return qtrue; + } + + //initialize the bot states + memset( botstates, 0, sizeof(botstates) ); + + errnum = BotInitLibrary(); + if (errnum != BLERR_NOERROR) return qfalse; + return qtrue; +} + +/* +============== +BotAIShutdown +============== +*/ +int BotAIShutdown( int restart ) { + + int i; + + //if the game is restarted for a tournament + if ( restart ) { + //shutdown all the bots in the botlib + for (i = 0; i < MAX_CLIENTS; i++) { + if (botstates[i] && botstates[i]->inuse) { + BotAIShutdownClient(botstates[i]->client, restart); + } + } + //don't shutdown the bot library + } + else { + trap_BotLibShutdown(); + } + return qtrue; +} + diff --git a/reaction/engine/code/game/ai_main.h b/reaction/engine/code/game/ai_main.h new file mode 100644 index 00000000..effa3064 --- /dev/null +++ b/reaction/engine/code/game/ai_main.h @@ -0,0 +1,299 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: ai_main.h + * + * desc: Quake3 bot AI + * + * $Archive: /source/code/botai/ai_chat.c $ + * + *****************************************************************************/ + +//#define DEBUG +#define CTF + +#define MAX_ITEMS 256 +//bot flags +#define BFL_STRAFERIGHT 1 //strafe to the right +#define BFL_ATTACKED 2 //bot has attacked last ai frame +#define BFL_ATTACKJUMPED 4 //bot jumped during attack last frame +#define BFL_AIMATENEMY 8 //bot aimed at the enemy this frame +#define BFL_AVOIDRIGHT 16 //avoid obstacles by going to the right +#define BFL_IDEALVIEWSET 32 //bot has ideal view angles set +#define BFL_FIGHTSUICIDAL 64 //bot is in a suicidal fight +//long term goal types +#define LTG_TEAMHELP 1 //help a team mate +#define LTG_TEAMACCOMPANY 2 //accompany a team mate +#define LTG_DEFENDKEYAREA 3 //defend a key area +#define LTG_GETFLAG 4 //get the enemy flag +#define LTG_RUSHBASE 5 //rush to the base +#define LTG_RETURNFLAG 6 //return the flag +#define LTG_CAMP 7 //camp somewhere +#define LTG_CAMPORDER 8 //ordered to camp somewhere +#define LTG_PATROL 9 //patrol +#define LTG_GETITEM 10 //get an item +#define LTG_KILL 11 //kill someone +#define LTG_HARVEST 12 //harvest skulls +#define LTG_ATTACKENEMYBASE 13 //attack the enemy base +#define LTG_MAKELOVE_UNDER 14 +#define LTG_MAKELOVE_ONTOP 15 +//some goal dedication times +#define TEAM_HELP_TIME 60 //1 minute teamplay help time +#define TEAM_ACCOMPANY_TIME 600 //10 minutes teamplay accompany time +#define TEAM_DEFENDKEYAREA_TIME 600 //10 minutes ctf defend base time +#define TEAM_CAMP_TIME 600 //10 minutes camping time +#define TEAM_PATROL_TIME 600 //10 minutes patrolling time +#define TEAM_LEAD_TIME 600 //10 minutes taking the lead +#define TEAM_GETITEM_TIME 60 //1 minute +#define TEAM_KILL_SOMEONE 180 //3 minute to kill someone +#define TEAM_ATTACKENEMYBASE_TIME 600 //10 minutes +#define TEAM_HARVEST_TIME 120 //2 minutes +#define CTF_GETFLAG_TIME 600 //10 minutes ctf get flag time +#define CTF_RUSHBASE_TIME 120 //2 minutes ctf rush base time +#define CTF_RETURNFLAG_TIME 180 //3 minutes to return the flag +#define CTF_ROAM_TIME 60 //1 minute ctf roam time +//patrol flags +#define PATROL_LOOP 1 +#define PATROL_REVERSE 2 +#define PATROL_BACK 4 +//teamplay task preference +#define TEAMTP_DEFENDER 1 +#define TEAMTP_ATTACKER 2 +//CTF strategy +#define CTFS_AGRESSIVE 1 +//copied from the aas file header +#define PRESENCE_NONE 1 +#define PRESENCE_NORMAL 2 +#define PRESENCE_CROUCH 4 +// +#define MAX_PROXMINES 64 + +//check points +typedef struct bot_waypoint_s +{ + int inuse; + char name[32]; + bot_goal_t goal; + struct bot_waypoint_s *next, *prev; +} bot_waypoint_t; + +#define MAX_ACTIVATESTACK 8 +#define MAX_ACTIVATEAREAS 32 + +typedef struct bot_activategoal_s +{ + int inuse; + bot_goal_t goal; //goal to activate (buttons etc.) + float time; //time to activate something + float start_time; //time starting to activate something + float justused_time; //time the goal was used + int shoot; //true if bot has to shoot to activate + int weapon; //weapon to be used for activation + vec3_t target; //target to shoot at to activate something + vec3_t origin; //origin of the blocking entity to activate + int areas[MAX_ACTIVATEAREAS]; //routing areas disabled by blocking entity + int numareas; //number of disabled routing areas + int areasdisabled; //true if the areas are disabled for the routing + struct bot_activategoal_s *next; //next activate goal on stack +} bot_activategoal_t; + +//bot state +typedef struct bot_state_s +{ + int inuse; //true if this state is used by a bot client + int botthink_residual; //residual for the bot thinks + int client; //client number of the bot + int entitynum; //entity number of the bot + playerState_t cur_ps; //current player state + int last_eFlags; //last ps flags + usercmd_t lastucmd; //usercmd from last frame + int entityeventTime[1024]; //last entity event time + // + bot_settings_t settings; //several bot settings + int (*ainode)(struct bot_state_s *bs); //current AI node + float thinktime; //time the bot thinks this frame + vec3_t origin; //origin of the bot + vec3_t velocity; //velocity of the bot + int presencetype; //presence type of the bot + vec3_t eye; //eye coordinates of the bot + int areanum; //the number of the area the bot is in + int inventory[MAX_ITEMS]; //string with items amounts the bot has + int tfl; //the travel flags the bot uses + int flags; //several flags + int respawn_wait; //wait until respawned + int lasthealth; //health value previous frame + int lastkilledplayer; //last killed player + int lastkilledby; //player that last killed this bot + int botdeathtype; //the death type of the bot + int enemydeathtype; //the death type of the enemy + int botsuicide; //true when the bot suicides + int enemysuicide; //true when the enemy of the bot suicides + int setupcount; //true when the bot has just been setup + int map_restart; //true when the map is being restarted + int entergamechat; //true when the bot used an enter game chat + int num_deaths; //number of time this bot died + int num_kills; //number of kills of this bot + int revenge_enemy; //the revenge enemy + int revenge_kills; //number of kills the enemy made + int lastframe_health; //health value the last frame + int lasthitcount; //number of hits last frame + int chatto; //chat to all or team + float walker; //walker charactertic + float ltime; //local bot time + float entergame_time; //time the bot entered the game + float ltg_time; //long term goal time + float nbg_time; //nearby goal time + float respawn_time; //time the bot takes to respawn + float respawnchat_time; //time the bot started a chat during respawn + float chase_time; //time the bot will chase the enemy + float enemyvisible_time; //time the enemy was last visible + float check_time; //time to check for nearby items + float stand_time; //time the bot is standing still + float lastchat_time; //time the bot last selected a chat + float kamikaze_time; //time to check for kamikaze usage + float invulnerability_time; //time to check for invulnerability usage + float standfindenemy_time; //time to find enemy while standing + float attackstrafe_time; //time the bot is strafing in one dir + float attackcrouch_time; //time the bot will stop crouching + float attackchase_time; //time the bot chases during actual attack + float attackjump_time; //time the bot jumped during attack + float enemysight_time; //time before reacting to enemy + float enemydeath_time; //time the enemy died + float enemyposition_time; //time the position and velocity of the enemy were stored + float defendaway_time; //time away while defending + float defendaway_range; //max travel time away from defend area + float rushbaseaway_time; //time away from rushing to the base + float attackaway_time; //time away from attacking the enemy base + float harvestaway_time; //time away from harvesting + float ctfroam_time; //time the bot is roaming in ctf + float killedenemy_time; //time the bot killed the enemy + float arrive_time; //time arrived (at companion) + float lastair_time; //last time the bot had air + float teleport_time; //last time the bot teleported + float camp_time; //last time camped + float camp_range; //camp range + float weaponchange_time; //time the bot started changing weapons + float firethrottlewait_time; //amount of time to wait + float firethrottleshoot_time; //amount of time to shoot + float notblocked_time; //last time the bot was not blocked + float blockedbyavoidspot_time; //time blocked by an avoid spot + float predictobstacles_time; //last time the bot predicted obstacles + int predictobstacles_goalareanum; //last goal areanum the bot predicted obstacles for + vec3_t aimtarget; + vec3_t enemyvelocity; //enemy velocity 0.5 secs ago during battle + vec3_t enemyorigin; //enemy origin 0.5 secs ago during battle + // + int kamikazebody; //kamikaze body + int proxmines[MAX_PROXMINES]; + int numproxmines; + // + int character; //the bot character + int ms; //move state of the bot + int gs; //goal state of the bot + int cs; //chat state of the bot + int ws; //weapon state of the bot + // + int enemy; //enemy entity number + int lastenemyareanum; //last reachability area the enemy was in + vec3_t lastenemyorigin; //last origin of the enemy in the reachability area + int weaponnum; //current weapon number + vec3_t viewangles; //current view angles + vec3_t ideal_viewangles; //ideal view angles + vec3_t viewanglespeed; + // + int ltgtype; //long term goal type + // team goals + int teammate; //team mate involved in this team goal + int decisionmaker; //player who decided to go for this goal + int ordered; //true if ordered to do something + float order_time; //time ordered to do something + int owndecision_time; //time the bot made it's own decision + bot_goal_t teamgoal; //the team goal + bot_goal_t altroutegoal; //alternative route goal + float reachedaltroutegoal_time; //time the bot reached the alt route goal + float teammessage_time; //time to message team mates what the bot is doing + float teamgoal_time; //time to stop helping team mate + float teammatevisible_time; //last time the team mate was NOT visible + int teamtaskpreference; //team task preference + // last ordered team goal + int lastgoal_decisionmaker; + int lastgoal_ltgtype; + int lastgoal_teammate; + bot_goal_t lastgoal_teamgoal; + // for leading team mates + int lead_teammate; //team mate the bot is leading + bot_goal_t lead_teamgoal; //team goal while leading + float lead_time; //time leading someone + float leadvisible_time; //last time the team mate was visible + float leadmessage_time; //last time a messaged was sent to the team mate + float leadbackup_time; //time backing up towards team mate + // + char teamleader[32]; //netname of the team leader + float askteamleader_time; //time asked for team leader + float becometeamleader_time; //time the bot will become the team leader + float teamgiveorders_time; //time to give team orders + float lastflagcapture_time; //last time a flag was captured + int numteammates; //number of team mates + int redflagstatus; //0 = at base, 1 = not at base + int blueflagstatus; //0 = at base, 1 = not at base + int neutralflagstatus; //0 = at base, 1 = our team has flag, 2 = enemy team has flag, 3 = enemy team dropped the flag + int flagstatuschanged; //flag status changed + int forceorders; //true if forced to give orders + int flagcarrier; //team mate carrying the enemy flag + int ctfstrategy; //ctf strategy + char subteam[32]; //sub team name + float formation_dist; //formation team mate intervening space + char formation_teammate[16]; //netname of the team mate the bot uses for relative positioning + float formation_angle; //angle relative to the formation team mate + vec3_t formation_dir; //the direction the formation is moving in + vec3_t formation_origin; //origin the bot uses for relative positioning + bot_goal_t formation_goal; //formation goal + + bot_activategoal_t *activatestack; //first activate goal on the stack + bot_activategoal_t activategoalheap[MAX_ACTIVATESTACK]; //activate goal heap + + bot_waypoint_t *checkpoints; //check points + bot_waypoint_t *patrolpoints; //patrol points + bot_waypoint_t *curpatrolpoint; //current patrol point the bot is going for + int patrolflags; //patrol flags +} bot_state_t; + +//resets the whole bot state +void BotResetState(bot_state_t *bs); +//returns the number of bots in the game +int NumBots(void); +//returns info about the entity +void BotEntityInfo(int entnum, aas_entityinfo_t *info); + +extern float floattime; +#define FloatTime() floattime + +// from the game source +void QDECL BotAI_Print(int type, char *fmt, ...); +void QDECL QDECL BotAI_BotInitialChat( bot_state_t *bs, char *type, ... ); +void BotAI_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask); +int BotAI_GetClientState( int clientNum, playerState_t *state ); +int BotAI_GetEntityState( int entityNum, entityState_t *state ); +int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state ); +int BotTeamLeader(bot_state_t *bs); diff --git a/reaction/engine/code/game/ai_team.c b/reaction/engine/code/game/ai_team.c new file mode 100644 index 00000000..858c16cd --- /dev/null +++ b/reaction/engine/code/game/ai_team.c @@ -0,0 +1,2080 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: ai_team.c + * + * desc: Quake3 bot AI + * + * $Archive: /MissionPack/code/game/ai_team.c $ + * + *****************************************************************************/ + +#include "g_local.h" +#include "../botlib/botlib.h" +#include "../botlib/be_aas.h" +#include "../botlib/be_ea.h" +#include "../botlib/be_ai_char.h" +#include "../botlib/be_ai_chat.h" +#include "../botlib/be_ai_gen.h" +#include "../botlib/be_ai_goal.h" +#include "../botlib/be_ai_move.h" +#include "../botlib/be_ai_weap.h" +// +#include "ai_main.h" +#include "ai_dmq3.h" +#include "ai_chat.h" +#include "ai_cmd.h" +#include "ai_dmnet.h" +#include "ai_team.h" +#include "ai_vcmd.h" + +#include "match.h" + +// for the voice chats +#include "../../ui/menudef.h" + +//ctf task preferences for a client +typedef struct bot_ctftaskpreference_s +{ + char name[36]; + int preference; +} bot_ctftaskpreference_t; + +bot_ctftaskpreference_t ctftaskpreferences[MAX_CLIENTS]; + + +/* +================== +BotValidTeamLeader +================== +*/ +int BotValidTeamLeader(bot_state_t *bs) { + if (!strlen(bs->teamleader)) return qfalse; + if (ClientFromName(bs->teamleader) == -1) return qfalse; + return qtrue; +} + +/* +================== +BotNumTeamMates +================== +*/ +int BotNumTeamMates(bot_state_t *bs) { + int i, numplayers; + char buf[MAX_INFO_STRING]; + static int maxclients; + + if (!maxclients) + maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); + + numplayers = 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; + // + if (BotSameTeam(bs, i)) { + numplayers++; + } + } + return numplayers; +} + +/* +================== +BotClientTravelTimeToGoal +================== +*/ +int BotClientTravelTimeToGoal(int client, bot_goal_t *goal) { + playerState_t ps; + int areanum; + + BotAI_GetClientState(client, &ps); + areanum = BotPointAreaNum(ps.origin); + if (!areanum) return 1; + return trap_AAS_AreaTravelTimeToGoalArea(areanum, ps.origin, goal->areanum, TFL_DEFAULT); +} + +/* +================== +BotSortTeamMatesByBaseTravelTime +================== +*/ +int BotSortTeamMatesByBaseTravelTime(bot_state_t *bs, int *teammates, int maxteammates) { + + int i, j, k, numteammates, traveltime; + char buf[MAX_INFO_STRING]; + static int maxclients; + int traveltimes[MAX_CLIENTS]; + bot_goal_t *goal = NULL; + + if (gametype == GT_CTF || gametype == GT_1FCTF) { + if (BotTeam(bs) == TEAM_RED) + goal = &ctf_redflag; + else + goal = &ctf_blueflag; + } +#ifdef MISSIONPACK + else { + if (BotTeam(bs) == TEAM_RED) + goal = &redobelisk; + else + goal = &blueobelisk; + } +#endif + if (!maxclients) + maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); + + numteammates = 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; + // + if (BotSameTeam(bs, i)) { + // + traveltime = BotClientTravelTimeToGoal(i, goal); + // + for (j = 0; j < numteammates; j++) { + if (traveltime < traveltimes[j]) { + for (k = numteammates; k > j; k--) { + traveltimes[k] = traveltimes[k-1]; + teammates[k] = teammates[k-1]; + } + break; + } + } + traveltimes[j] = traveltime; + teammates[j] = i; + numteammates++; + if (numteammates >= maxteammates) break; + } + } + return numteammates; +} + +/* +================== +BotSetTeamMateTaskPreference +================== +*/ +void BotSetTeamMateTaskPreference(bot_state_t *bs, int teammate, int preference) { + char teammatename[MAX_NETNAME]; + + ctftaskpreferences[teammate].preference = preference; + ClientName(teammate, teammatename, sizeof(teammatename)); + strcpy(ctftaskpreferences[teammate].name, teammatename); +} + +/* +================== +BotGetTeamMateTaskPreference +================== +*/ +int BotGetTeamMateTaskPreference(bot_state_t *bs, int teammate) { + char teammatename[MAX_NETNAME]; + + if (!ctftaskpreferences[teammate].preference) return 0; + ClientName(teammate, teammatename, sizeof(teammatename)); + if (Q_stricmp(teammatename, ctftaskpreferences[teammate].name)) return 0; + return ctftaskpreferences[teammate].preference; +} + +/* +================== +BotSortTeamMatesByTaskPreference +================== +*/ +int BotSortTeamMatesByTaskPreference(bot_state_t *bs, int *teammates, int numteammates) { + int defenders[MAX_CLIENTS], numdefenders; + int attackers[MAX_CLIENTS], numattackers; + int roamers[MAX_CLIENTS], numroamers; + int i, preference; + + numdefenders = numattackers = numroamers = 0; + for (i = 0; i < numteammates; i++) { + preference = BotGetTeamMateTaskPreference(bs, teammates[i]); + if (preference & TEAMTP_DEFENDER) { + defenders[numdefenders++] = teammates[i]; + } + else if (preference & TEAMTP_ATTACKER) { + attackers[numattackers++] = teammates[i]; + } + else { + roamers[numroamers++] = teammates[i]; + } + } + numteammates = 0; + //defenders at the front of the list + memcpy(&teammates[numteammates], defenders, numdefenders * sizeof(int)); + numteammates += numdefenders; + //roamers in the middle + memcpy(&teammates[numteammates], roamers, numroamers * sizeof(int)); + numteammates += numroamers; + //attacker in the back of the list + memcpy(&teammates[numteammates], attackers, numattackers * sizeof(int)); + numteammates += numattackers; + + return numteammates; +} + +/* +================== +BotSayTeamOrders +================== +*/ +void BotSayTeamOrderAlways(bot_state_t *bs, int toclient) { + char teamchat[MAX_MESSAGE_SIZE]; + char buf[MAX_MESSAGE_SIZE]; + char name[MAX_NETNAME]; + + //if the bot is talking to itself + if (bs->client == toclient) { + //don't show the message just put it in the console message queue + trap_BotGetChatMessage(bs->cs, buf, sizeof(buf)); + ClientName(bs->client, name, sizeof(name)); + Com_sprintf(teamchat, sizeof(teamchat), EC"(%s"EC")"EC": %s", name, buf); + trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, teamchat); + } + else { + trap_BotEnterChat(bs->cs, toclient, CHAT_TELL); + } +} + +/* +================== +BotSayTeamOrders +================== +*/ +void BotSayTeamOrder(bot_state_t *bs, int toclient) { +#ifdef MISSIONPACK + // voice chats only + char buf[MAX_MESSAGE_SIZE]; + + trap_BotGetChatMessage(bs->cs, buf, sizeof(buf)); +#else + BotSayTeamOrderAlways(bs, toclient); +#endif +} + +/* +================== +BotVoiceChat +================== +*/ +void BotVoiceChat(bot_state_t *bs, int toclient, char *voicechat) { +#ifdef MISSIONPACK + if (toclient == -1) + // voice only say team + trap_EA_Command(bs->client, va("vsay_team %s", voicechat)); + else + // voice only tell single player + trap_EA_Command(bs->client, va("vtell %d %s", toclient, voicechat)); +#endif +} + +/* +================== +BotVoiceChatOnly +================== +*/ +void BotVoiceChatOnly(bot_state_t *bs, int toclient, char *voicechat) { +#ifdef MISSIONPACK + if (toclient == -1) + // voice only say team + trap_EA_Command(bs->client, va("vosay_team %s", voicechat)); + else + // voice only tell single player + trap_EA_Command(bs->client, va("votell %d %s", toclient, voicechat)); +#endif +} + +/* +================== +BotSayVoiceTeamOrder +================== +*/ +void BotSayVoiceTeamOrder(bot_state_t *bs, int toclient, char *voicechat) { +#ifdef MISSIONPACK + BotVoiceChat(bs, toclient, voicechat); +#endif +} + +/* +================== +BotCTFOrders +================== +*/ +void BotCTFOrders_BothFlagsNotAtBase(bot_state_t *bs) { + int numteammates, defenders, attackers, i, other; + int teammates[MAX_CLIENTS]; + char name[MAX_NETNAME], carriername[MAX_NETNAME]; + + numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); + BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); + //different orders based on the number of team mates + switch(bs->numteammates) { + case 1: break; + case 2: + { + //tell the one not carrying the flag to attack the enemy base + if (teammates[0] != bs->flagcarrier) other = teammates[0]; + else other = teammates[1]; + ClientName(other, name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, other); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_GETFLAG); + break; + } + case 3: + { + //tell the one closest to the base not carrying the flag to accompany the flag carrier + if (teammates[0] != bs->flagcarrier) other = teammates[0]; + else other = teammates[1]; + ClientName(other, name, sizeof(name)); + if ( bs->flagcarrier != -1 ) { + ClientName(bs->flagcarrier, carriername, sizeof(carriername)); + if (bs->flagcarrier == bs->client) { + BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_FOLLOWME); + } + else { + BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_FOLLOWFLAGCARRIER); + } + } + else { + // + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_GETFLAG); + } + BotSayTeamOrder(bs, other); + //tell the one furthest from the the base not carrying the flag to get the enemy flag + if (teammates[2] != bs->flagcarrier) other = teammates[2]; + else other = teammates[1]; + ClientName(other, name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, other); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_RETURNFLAG); + break; + } + default: + { + defenders = (int) (float) numteammates * 0.4 + 0.5; + if (defenders > 4) defenders = 4; + attackers = (int) (float) numteammates * 0.5 + 0.5; + if (attackers > 5) attackers = 5; + if (bs->flagcarrier != -1) { + ClientName(bs->flagcarrier, carriername, sizeof(carriername)); + for (i = 0; i < defenders; i++) { + // + if (teammates[i] == bs->flagcarrier) { + continue; + } + // + ClientName(teammates[i], name, sizeof(name)); + if (bs->flagcarrier == bs->client) { + BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_FOLLOWME); + } + else { + BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_FOLLOWFLAGCARRIER); + } + BotSayTeamOrder(bs, teammates[i]); + } + } + else { + for (i = 0; i < defenders; i++) { + // + if (teammates[i] == bs->flagcarrier) { + continue; + } + // + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_GETFLAG); + BotSayTeamOrder(bs, teammates[i]); + } + } + for (i = 0; i < attackers; i++) { + // + if (teammates[numteammates - i - 1] == bs->flagcarrier) { + continue; + } + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_RETURNFLAG); + } + // + break; + } + } +} + +/* +================== +BotCTFOrders +================== +*/ +void BotCTFOrders_FlagNotAtBase(bot_state_t *bs) { + int numteammates, defenders, attackers, i; + int teammates[MAX_CLIENTS]; + char name[MAX_NETNAME]; + + numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); + BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); + //passive strategy + if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { + //different orders based on the number of team mates + switch(bs->numteammates) { + case 1: break; + case 2: + { + //both will go for the enemy flag + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_GETFLAG); + // + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); + break; + } + case 3: + { + //keep one near the base for when the flag is returned + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the other two get the flag + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); + // + ClientName(teammates[2], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[2]); + BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); + break; + } + default: + { + //keep some people near the base for when the flag is returned + defenders = (int) (float) numteammates * 0.3 + 0.5; + if (defenders > 3) defenders = 3; + attackers = (int) (float) numteammates * 0.7 + 0.5; + if (attackers > 6) attackers = 6; + for (i = 0; i < defenders; i++) { + // + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + for (i = 0; i < attackers; i++) { + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_GETFLAG); + } + // + break; + } + } + } + else { + //different orders based on the number of team mates + switch(bs->numteammates) { + case 1: break; + case 2: + { + //both will go for the enemy flag + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_GETFLAG); + // + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); + break; + } + case 3: + { + //everyone go for the flag + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_GETFLAG); + // + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); + // + ClientName(teammates[2], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[2]); + BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); + break; + } + default: + { + //keep some people near the base for when the flag is returned + defenders = (int) (float) numteammates * 0.2 + 0.5; + if (defenders > 2) defenders = 2; + attackers = (int) (float) numteammates * 0.7 + 0.5; + if (attackers > 7) attackers = 7; + for (i = 0; i < defenders; i++) { + // + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + for (i = 0; i < attackers; i++) { + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); + } + // + break; + } + } + } +} + +/* +================== +BotCTFOrders +================== +*/ +void BotCTFOrders_EnemyFlagNotAtBase(bot_state_t *bs) { + int numteammates, defenders, attackers, i, other; + int teammates[MAX_CLIENTS]; + char name[MAX_NETNAME], carriername[MAX_NETNAME]; + + numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); + BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); + //different orders based on the number of team mates + switch(numteammates) { + case 1: break; + case 2: + { + //tell the one not carrying the flag to defend the base + if (teammates[0] == bs->flagcarrier) other = teammates[1]; + else other = teammates[0]; + ClientName(other, name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, other); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_DEFEND); + break; + } + case 3: + { + //tell the one closest to the base not carrying the flag to defend the base + if (teammates[0] != bs->flagcarrier) other = teammates[0]; + else other = teammates[1]; + ClientName(other, name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, other); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_DEFEND); + //tell the other also to defend the base + if (teammates[2] != bs->flagcarrier) other = teammates[2]; + else other = teammates[1]; + ClientName(other, name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, other); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_DEFEND); + break; + } + default: + { + //60% will defend the base + defenders = (int) (float) numteammates * 0.6 + 0.5; + if (defenders > 6) defenders = 6; + //30% accompanies the flag carrier + attackers = (int) (float) numteammates * 0.3 + 0.5; + if (attackers > 3) attackers = 3; + for (i = 0; i < defenders; i++) { + // + if (teammates[i] == bs->flagcarrier) { + continue; + } + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + // if we have a flag carrier + if ( bs->flagcarrier != -1 ) { + ClientName(bs->flagcarrier, carriername, sizeof(carriername)); + for (i = 0; i < attackers; i++) { + // + if (teammates[numteammates - i - 1] == bs->flagcarrier) { + continue; + } + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + if (bs->flagcarrier == bs->client) { + BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_FOLLOWME); + } + else { + BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_FOLLOWFLAGCARRIER); + } + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + } + } + else { + for (i = 0; i < attackers; i++) { + // + if (teammates[numteammates - i - 1] == bs->flagcarrier) { + continue; + } + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + } + } + // + break; + } + } +} + + +/* +================== +BotCTFOrders +================== +*/ +void BotCTFOrders_BothFlagsAtBase(bot_state_t *bs) { + int numteammates, defenders, attackers, i; + int teammates[MAX_CLIENTS]; + char name[MAX_NETNAME]; + + //sort team mates by travel time to base + numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); + //sort team mates by CTF preference + BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); + //passive strategy + if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { + //different orders based on the number of team mates + switch(numteammates) { + case 1: break; + case 2: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the other will get the flag + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); + break; + } + case 3: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the second one closest to the base will defend the base + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); + //the other will get the flag + ClientName(teammates[2], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[2]); + BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); + break; + } + default: + { + defenders = (int) (float) numteammates * 0.5 + 0.5; + if (defenders > 5) defenders = 5; + attackers = (int) (float) numteammates * 0.4 + 0.5; + if (attackers > 4) attackers = 4; + for (i = 0; i < defenders; i++) { + // + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + for (i = 0; i < attackers; i++) { + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); + } + // + break; + } + } + } + else { + //different orders based on the number of team mates + switch(numteammates) { + case 1: break; + case 2: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the other will get the flag + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); + break; + } + case 3: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the others should go for the enemy flag + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); + // + ClientName(teammates[2], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[2]); + BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); + break; + } + default: + { + defenders = (int) (float) numteammates * 0.4 + 0.5; + if (defenders > 4) defenders = 4; + attackers = (int) (float) numteammates * 0.5 + 0.5; + if (attackers > 5) attackers = 5; + for (i = 0; i < defenders; i++) { + // + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + for (i = 0; i < attackers; i++) { + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); + } + // + break; + } + } + } +} + +/* +================== +BotCTFOrders +================== +*/ +void BotCTFOrders(bot_state_t *bs) { + int flagstatus; + + // + if (BotTeam(bs) == TEAM_RED) flagstatus = bs->redflagstatus * 2 + bs->blueflagstatus; + else flagstatus = bs->blueflagstatus * 2 + bs->redflagstatus; + // + switch(flagstatus) { + case 0: BotCTFOrders_BothFlagsAtBase(bs); break; + case 1: BotCTFOrders_EnemyFlagNotAtBase(bs); break; + case 2: BotCTFOrders_FlagNotAtBase(bs); break; + case 3: BotCTFOrders_BothFlagsNotAtBase(bs); break; + } +} + + +/* +================== +BotCreateGroup +================== +*/ +void BotCreateGroup(bot_state_t *bs, int *teammates, int groupsize) { + char name[MAX_NETNAME], leadername[MAX_NETNAME]; + int i; + + // the others in the group will follow the teammates[0] + ClientName(teammates[0], leadername, sizeof(leadername)); + for (i = 1; i < groupsize; i++) + { + ClientName(teammates[i], name, sizeof(name)); + if (teammates[0] == bs->client) { + BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); + } + else { + BotAI_BotInitialChat(bs, "cmd_accompany", name, leadername, NULL); + } + BotSayTeamOrderAlways(bs, teammates[i]); + } +} + +/* +================== +BotTeamOrders + + FIXME: defend key areas? +================== +*/ +void BotTeamOrders(bot_state_t *bs) { + int teammates[MAX_CLIENTS]; + int numteammates, i; + char buf[MAX_INFO_STRING]; + static int maxclients; + + if (!maxclients) + maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); + + numteammates = 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; + // + if (BotSameTeam(bs, i)) { + teammates[numteammates] = i; + numteammates++; + } + } + // + switch(numteammates) { + case 1: break; + case 2: + { + //nothing special + break; + } + case 3: + { + //have one follow another and one free roaming + BotCreateGroup(bs, teammates, 2); + break; + } + case 4: + { + BotCreateGroup(bs, teammates, 2); //a group of 2 + BotCreateGroup(bs, &teammates[2], 2); //a group of 2 + break; + } + case 5: + { + BotCreateGroup(bs, teammates, 2); //a group of 2 + BotCreateGroup(bs, &teammates[2], 3); //a group of 3 + break; + } + default: + { + if (numteammates <= 10) { + for (i = 0; i < numteammates / 2; i++) { + BotCreateGroup(bs, &teammates[i*2], 2); //groups of 2 + } + } + break; + } + } +} + +#ifdef MISSIONPACK + +/* +================== +Bot1FCTFOrders_FlagAtCenter + + X% defend the base, Y% get the flag +================== +*/ +void Bot1FCTFOrders_FlagAtCenter(bot_state_t *bs) { + int numteammates, defenders, attackers, i; + int teammates[MAX_CLIENTS]; + char name[MAX_NETNAME]; + + //sort team mates by travel time to base + numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); + //sort team mates by CTF preference + BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); + //passive strategy + if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { + //different orders based on the number of team mates + switch(numteammates) { + case 1: break; + case 2: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the other will get the flag + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); + break; + } + case 3: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the second one closest to the base will defend the base + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the other will get the flag + ClientName(teammates[2], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[2]); + BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); + break; + } + default: + { + //50% defend the base + defenders = (int) (float) numteammates * 0.5 + 0.5; + if (defenders > 5) defenders = 5; + //40% get the flag + attackers = (int) (float) numteammates * 0.4 + 0.5; + if (attackers > 4) attackers = 4; + for (i = 0; i < defenders; i++) { + // + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + for (i = 0; i < attackers; i++) { + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); + } + // + break; + } + } + } + else { //agressive + //different orders based on the number of team mates + switch(numteammates) { + case 1: break; + case 2: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the other will get the flag + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); + break; + } + case 3: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the others should go for the enemy flag + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); + // + ClientName(teammates[2], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[2]); + BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); + break; + } + default: + { + //30% defend the base + defenders = (int) (float) numteammates * 0.3 + 0.5; + if (defenders > 3) defenders = 3; + //60% get the flag + attackers = (int) (float) numteammates * 0.6 + 0.5; + if (attackers > 6) attackers = 6; + for (i = 0; i < defenders; i++) { + // + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + for (i = 0; i < attackers; i++) { + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); + } + // + break; + } + } + } +} + +/* +================== +Bot1FCTFOrders_TeamHasFlag + + X% towards neutral flag, Y% go towards enemy base and accompany flag carrier if visible +================== +*/ +void Bot1FCTFOrders_TeamHasFlag(bot_state_t *bs) { + int numteammates, defenders, attackers, i, other; + int teammates[MAX_CLIENTS]; + char name[MAX_NETNAME], carriername[MAX_NETNAME]; + + //sort team mates by travel time to base + numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); + //sort team mates by CTF preference + BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); + //passive strategy + if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { + //different orders based on the number of team mates + switch(numteammates) { + case 1: break; + case 2: + { + //tell the one not carrying the flag to attack the enemy base + if (teammates[0] == bs->flagcarrier) other = teammates[1]; + else other = teammates[0]; + ClientName(other, name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); + BotSayTeamOrder(bs, other); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_OFFENSE); + break; + } + case 3: + { + //tell the one closest to the base not carrying the flag to defend the base + if (teammates[0] != bs->flagcarrier) other = teammates[0]; + else other = teammates[1]; + ClientName(other, name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, other); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_DEFEND); + //tell the one furthest from the base not carrying the flag to accompany the flag carrier + if (teammates[2] != bs->flagcarrier) other = teammates[2]; + else other = teammates[1]; + ClientName(other, name, sizeof(name)); + if ( bs->flagcarrier != -1 ) { + ClientName(bs->flagcarrier, carriername, sizeof(carriername)); + if (bs->flagcarrier == bs->client) { + BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_FOLLOWME); + } + else { + BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_FOLLOWFLAGCARRIER); + } + } + else { + // + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_GETFLAG); + } + BotSayTeamOrder(bs, other); + break; + } + default: + { + //30% will defend the base + defenders = (int) (float) numteammates * 0.3 + 0.5; + if (defenders > 3) defenders = 3; + //70% accompanies the flag carrier + attackers = (int) (float) numteammates * 0.7 + 0.5; + if (attackers > 7) attackers = 7; + for (i = 0; i < defenders; i++) { + // + if (teammates[i] == bs->flagcarrier) { + continue; + } + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + if (bs->flagcarrier != -1) { + ClientName(bs->flagcarrier, carriername, sizeof(carriername)); + for (i = 0; i < attackers; i++) { + // + if (teammates[numteammates - i - 1] == bs->flagcarrier) { + continue; + } + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + if (bs->flagcarrier == bs->client) { + BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_FOLLOWME); + } + else { + BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_FOLLOWFLAGCARRIER); + } + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + } + } + else { + for (i = 0; i < attackers; i++) { + // + if (teammates[numteammates - i - 1] == bs->flagcarrier) { + continue; + } + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); + } + } + // + break; + } + } + } + else { //agressive + //different orders based on the number of team mates + switch(numteammates) { + case 1: break; + case 2: + { + //tell the one not carrying the flag to defend the base + if (teammates[0] == bs->flagcarrier) other = teammates[1]; + else other = teammates[0]; + ClientName(other, name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, other); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_DEFEND); + break; + } + case 3: + { + //tell the one closest to the base not carrying the flag to defend the base + if (teammates[0] != bs->flagcarrier) other = teammates[0]; + else other = teammates[1]; + ClientName(other, name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, other); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_DEFEND); + //tell the one furthest from the base not carrying the flag to accompany the flag carrier + if (teammates[2] != bs->flagcarrier) other = teammates[2]; + else other = teammates[1]; + ClientName(other, name, sizeof(name)); + ClientName(bs->flagcarrier, carriername, sizeof(carriername)); + if (bs->flagcarrier == bs->client) { + BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_FOLLOWME); + } + else { + BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); + BotSayVoiceTeamOrder(bs, other, VOICECHAT_FOLLOWFLAGCARRIER); + } + BotSayTeamOrder(bs, other); + break; + } + default: + { + //20% will defend the base + defenders = (int) (float) numteammates * 0.2 + 0.5; + if (defenders > 2) defenders = 2; + //80% accompanies the flag carrier + attackers = (int) (float) numteammates * 0.8 + 0.5; + if (attackers > 8) attackers = 8; + for (i = 0; i < defenders; i++) { + // + if (teammates[i] == bs->flagcarrier) { + continue; + } + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + ClientName(bs->flagcarrier, carriername, sizeof(carriername)); + for (i = 0; i < attackers; i++) { + // + if (teammates[numteammates - i - 1] == bs->flagcarrier) { + continue; + } + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + if (bs->flagcarrier == bs->client) { + BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_FOLLOWME); + } + else { + BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_FOLLOWFLAGCARRIER); + } + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + } + // + break; + } + } + } +} + +/* +================== +Bot1FCTFOrders_EnemyHasFlag + + X% defend the base, Y% towards neutral flag +================== +*/ +void Bot1FCTFOrders_EnemyHasFlag(bot_state_t *bs) { + int numteammates, defenders, attackers, i; + int teammates[MAX_CLIENTS]; + char name[MAX_NETNAME]; + + //sort team mates by travel time to base + numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); + //sort team mates by CTF preference + BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); + //passive strategy + if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { + //different orders based on the number of team mates + switch(numteammates) { + case 1: break; + case 2: + { + //both defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + // + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); + break; + } + case 3: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the second one closest to the base will defend the base + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); + //the other will also defend the base + ClientName(teammates[2], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[2]); + BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_DEFEND); + break; + } + default: + { + //80% will defend the base + defenders = (int) (float) numteammates * 0.8 + 0.5; + if (defenders > 8) defenders = 8; + //10% will try to return the flag + attackers = (int) (float) numteammates * 0.1 + 0.5; + if (attackers > 2) attackers = 2; + for (i = 0; i < defenders; i++) { + // + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + for (i = 0; i < attackers; i++) { + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_returnflag", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); + } + // + break; + } + } + } + else { //agressive + //different orders based on the number of team mates + switch(numteammates) { + case 1: break; + case 2: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the other will get the flag + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); + break; + } + case 3: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the others should go for the enemy flag + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); + // + ClientName(teammates[2], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_returnflag", name, NULL); + BotSayTeamOrder(bs, teammates[2]); + BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); + break; + } + default: + { + //70% defend the base + defenders = (int) (float) numteammates * 0.7 + 0.5; + if (defenders > 8) defenders = 8; + //20% try to return the flag + attackers = (int) (float) numteammates * 0.2 + 0.5; + if (attackers > 2) attackers = 2; + for (i = 0; i < defenders; i++) { + // + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + for (i = 0; i < attackers; i++) { + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_returnflag", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); + } + // + break; + } + } + } +} + +/* +================== +Bot1FCTFOrders_EnemyDroppedFlag + + X% defend the base, Y% get the flag +================== +*/ +void Bot1FCTFOrders_EnemyDroppedFlag(bot_state_t *bs) { + int numteammates, defenders, attackers, i; + int teammates[MAX_CLIENTS]; + char name[MAX_NETNAME]; + + //sort team mates by travel time to base + numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); + //sort team mates by CTF preference + BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); + //passive strategy + if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { + //different orders based on the number of team mates + switch(numteammates) { + case 1: break; + case 2: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the other will get the flag + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); + break; + } + case 3: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the second one closest to the base will defend the base + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); + //the other will get the flag + ClientName(teammates[2], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[2]); + BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); + break; + } + default: + { + //50% defend the base + defenders = (int) (float) numteammates * 0.5 + 0.5; + if (defenders > 5) defenders = 5; + //40% get the flag + attackers = (int) (float) numteammates * 0.4 + 0.5; + if (attackers > 4) attackers = 4; + for (i = 0; i < defenders; i++) { + // + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + for (i = 0; i < attackers; i++) { + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); + } + // + break; + } + } + } + else { //agressive + //different orders based on the number of team mates + switch(numteammates) { + case 1: break; + case 2: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the other will get the flag + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); + break; + } + case 3: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the others should go for the enemy flag + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); + // + ClientName(teammates[2], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[2]); + BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); + break; + } + default: + { + //30% defend the base + defenders = (int) (float) numteammates * 0.3 + 0.5; + if (defenders > 3) defenders = 3; + //60% get the flag + attackers = (int) (float) numteammates * 0.6 + 0.5; + if (attackers > 6) attackers = 6; + for (i = 0; i < defenders; i++) { + // + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + for (i = 0; i < attackers; i++) { + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_DEFEND); + } + // + break; + } + } + } +} + +/* +================== +Bot1FCTFOrders +================== +*/ +void Bot1FCTFOrders(bot_state_t *bs) { + switch(bs->neutralflagstatus) { + case 0: Bot1FCTFOrders_FlagAtCenter(bs); break; + case 1: Bot1FCTFOrders_TeamHasFlag(bs); break; + case 2: Bot1FCTFOrders_EnemyHasFlag(bs); break; + case 3: Bot1FCTFOrders_EnemyDroppedFlag(bs); break; + } +} + +/* +================== +BotObeliskOrders + + X% in defence Y% in offence +================== +*/ +void BotObeliskOrders(bot_state_t *bs) { + int numteammates, defenders, attackers, i; + int teammates[MAX_CLIENTS]; + char name[MAX_NETNAME]; + + //sort team mates by travel time to base + numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); + //sort team mates by CTF preference + BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); + //passive strategy + if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { + //different orders based on the number of team mates + switch(numteammates) { + case 1: break; + case 2: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the other will attack the enemy base + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_OFFENSE); + break; + } + case 3: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the one second closest to the base also defends the base + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); + //the other one attacks the enemy base + ClientName(teammates[2], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); + BotSayTeamOrder(bs, teammates[2]); + BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_OFFENSE); + break; + } + default: + { + //50% defend the base + defenders = (int) (float) numteammates * 0.5 + 0.5; + if (defenders > 5) defenders = 5; + //40% attack the enemy base + attackers = (int) (float) numteammates * 0.4 + 0.5; + if (attackers > 4) attackers = 4; + for (i = 0; i < defenders; i++) { + // + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + for (i = 0; i < attackers; i++) { + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_OFFENSE); + } + // + break; + } + } + } + else { + //different orders based on the number of team mates + switch(numteammates) { + case 1: break; + case 2: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the other will attack the enemy base + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_OFFENSE); + break; + } + case 3: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the others attack the enemy base + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_OFFENSE); + // + ClientName(teammates[2], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); + BotSayTeamOrder(bs, teammates[2]); + BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_OFFENSE); + break; + } + default: + { + //30% defend the base + defenders = (int) (float) numteammates * 0.3 + 0.5; + if (defenders > 3) defenders = 3; + //70% attack the enemy base + attackers = (int) (float) numteammates * 0.7 + 0.5; + if (attackers > 7) attackers = 7; + for (i = 0; i < defenders; i++) { + // + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + for (i = 0; i < attackers; i++) { + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_OFFENSE); + } + // + break; + } + } + } +} + +/* +================== +BotHarvesterOrders + + X% defend the base, Y% harvest +================== +*/ +void BotHarvesterOrders(bot_state_t *bs) { + int numteammates, defenders, attackers, i; + int teammates[MAX_CLIENTS]; + char name[MAX_NETNAME]; + + //sort team mates by travel time to base + numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); + //sort team mates by CTF preference + BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); + //passive strategy + if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { + //different orders based on the number of team mates + switch(numteammates) { + case 1: break; + case 2: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the other will harvest + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_harvest", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_OFFENSE); + break; + } + case 3: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the one second closest to the base also defends the base + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); + //the other one goes harvesting + ClientName(teammates[2], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_harvest", name, NULL); + BotSayTeamOrder(bs, teammates[2]); + BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_OFFENSE); + break; + } + default: + { + //50% defend the base + defenders = (int) (float) numteammates * 0.5 + 0.5; + if (defenders > 5) defenders = 5; + //40% goes harvesting + attackers = (int) (float) numteammates * 0.4 + 0.5; + if (attackers > 4) attackers = 4; + for (i = 0; i < defenders; i++) { + // + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + for (i = 0; i < attackers; i++) { + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_harvest", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_OFFENSE); + } + // + break; + } + } + } + else { + //different orders based on the number of team mates + switch(numteammates) { + case 1: break; + case 2: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the other will harvest + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_harvest", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_OFFENSE); + break; + } + case 3: + { + //the one closest to the base will defend the base + ClientName(teammates[0], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[0]); + BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); + //the others go harvesting + ClientName(teammates[1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_harvest", name, NULL); + BotSayTeamOrder(bs, teammates[1]); + BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_OFFENSE); + // + ClientName(teammates[2], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_harvest", name, NULL); + BotSayTeamOrder(bs, teammates[2]); + BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_OFFENSE); + break; + } + default: + { + //30% defend the base + defenders = (int) (float) numteammates * 0.3 + 0.5; + if (defenders > 3) defenders = 3; + //70% go harvesting + attackers = (int) (float) numteammates * 0.7 + 0.5; + if (attackers > 7) attackers = 7; + for (i = 0; i < defenders; i++) { + // + ClientName(teammates[i], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); + BotSayTeamOrder(bs, teammates[i]); + BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); + } + for (i = 0; i < attackers; i++) { + // + ClientName(teammates[numteammates - i - 1], name, sizeof(name)); + BotAI_BotInitialChat(bs, "cmd_harvest", name, NULL); + BotSayTeamOrder(bs, teammates[numteammates - i - 1]); + BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_OFFENSE); + } + // + break; + } + } + } +} +#endif + +/* +================== +FindHumanTeamLeader +================== +*/ +int FindHumanTeamLeader(bot_state_t *bs) { + int i; + + for (i = 0; i < MAX_CLIENTS; i++) { + if ( g_entities[i].inuse ) { + // if this player is not a bot + if ( !(g_entities[i].r.svFlags & SVF_BOT) ) { + // if this player is ok with being the leader + if (!notleader[i]) { + // if this player is on the same team + if ( BotSameTeam(bs, i) ) { + ClientName(i, bs->teamleader, sizeof(bs->teamleader)); + // if not yet ordered to do anything + if ( !BotSetLastOrderedTask(bs) ) { + // go on defense by default + BotVoiceChat_Defend(bs, i, SAY_TELL); + } + return qtrue; + } + } + } + } + } + return qfalse; +} + +/* +================== +BotTeamAI +================== +*/ +void BotTeamAI(bot_state_t *bs) { + int numteammates; + char netname[MAX_NETNAME]; + + // + if ( gametype < GT_TEAM ) + return; + // make sure we've got a valid team leader + if (!BotValidTeamLeader(bs)) { + // + if (!FindHumanTeamLeader(bs)) { + // + if (!bs->askteamleader_time && !bs->becometeamleader_time) { + if (bs->entergame_time + 10 > FloatTime()) { + bs->askteamleader_time = FloatTime() + 5 + random() * 10; + } + else { + bs->becometeamleader_time = FloatTime() + 5 + random() * 10; + } + } + if (bs->askteamleader_time && bs->askteamleader_time < FloatTime()) { + // if asked for a team leader and no response + BotAI_BotInitialChat(bs, "whoisteamleader", NULL); + trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); + bs->askteamleader_time = 0; + bs->becometeamleader_time = FloatTime() + 8 + random() * 10; + } + if (bs->becometeamleader_time && bs->becometeamleader_time < FloatTime()) { + BotAI_BotInitialChat(bs, "iamteamleader", NULL); + trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); + BotSayVoiceTeamOrder(bs, -1, VOICECHAT_STARTLEADER); + ClientName(bs->client, netname, sizeof(netname)); + strncpy(bs->teamleader, netname, sizeof(bs->teamleader)); + bs->teamleader[sizeof(bs->teamleader)-1] = '\0'; + bs->becometeamleader_time = 0; + } + return; + } + } + bs->askteamleader_time = 0; + bs->becometeamleader_time = 0; + + //return if this bot is NOT the team leader + ClientName(bs->client, netname, sizeof(netname)); + if (Q_stricmp(netname, bs->teamleader) != 0) return; + // + numteammates = BotNumTeamMates(bs); + //give orders + switch(gametype) { + case GT_TEAM: + { + if (bs->numteammates != numteammates || bs->forceorders) { + bs->teamgiveorders_time = FloatTime(); + bs->numteammates = numteammates; + bs->forceorders = qfalse; + } + //if it's time to give orders + if (bs->teamgiveorders_time && bs->teamgiveorders_time < FloatTime() - 5) { + BotTeamOrders(bs); + //give orders again after 120 seconds + bs->teamgiveorders_time = FloatTime() + 120; + } + break; + } + case GT_CTF: + { + //if the number of team mates changed or the flag status changed + //or someone wants to know what to do + if (bs->numteammates != numteammates || bs->flagstatuschanged || bs->forceorders) { + bs->teamgiveorders_time = FloatTime(); + bs->numteammates = numteammates; + bs->flagstatuschanged = qfalse; + bs->forceorders = qfalse; + } + //if there were no flag captures the last 3 minutes + if (bs->lastflagcapture_time < FloatTime() - 240) { + bs->lastflagcapture_time = FloatTime(); + //randomly change the CTF strategy + if (random() < 0.4) { + bs->ctfstrategy ^= CTFS_AGRESSIVE; + bs->teamgiveorders_time = FloatTime(); + } + } + //if it's time to give orders + if (bs->teamgiveorders_time && bs->teamgiveorders_time < FloatTime() - 3) { + BotCTFOrders(bs); + // + bs->teamgiveorders_time = 0; + } + break; + } +#ifdef MISSIONPACK + case GT_1FCTF: + { + if (bs->numteammates != numteammates || bs->flagstatuschanged || bs->forceorders) { + bs->teamgiveorders_time = FloatTime(); + bs->numteammates = numteammates; + bs->flagstatuschanged = qfalse; + bs->forceorders = qfalse; + } + //if there were no flag captures the last 4 minutes + if (bs->lastflagcapture_time < FloatTime() - 240) { + bs->lastflagcapture_time = FloatTime(); + //randomly change the CTF strategy + if (random() < 0.4) { + bs->ctfstrategy ^= CTFS_AGRESSIVE; + bs->teamgiveorders_time = FloatTime(); + } + } + //if it's time to give orders + if (bs->teamgiveorders_time && bs->teamgiveorders_time < FloatTime() - 2) { + Bot1FCTFOrders(bs); + // + bs->teamgiveorders_time = 0; + } + break; + } + case GT_OBELISK: + { + if (bs->numteammates != numteammates || bs->forceorders) { + bs->teamgiveorders_time = FloatTime(); + bs->numteammates = numteammates; + bs->forceorders = qfalse; + } + //if it's time to give orders + if (bs->teamgiveorders_time && bs->teamgiveorders_time < FloatTime() - 5) { + BotObeliskOrders(bs); + //give orders again after 30 seconds + bs->teamgiveorders_time = FloatTime() + 30; + } + break; + } + case GT_HARVESTER: + { + if (bs->numteammates != numteammates || bs->forceorders) { + bs->teamgiveorders_time = FloatTime(); + bs->numteammates = numteammates; + bs->forceorders = qfalse; + } + //if it's time to give orders + if (bs->teamgiveorders_time && bs->teamgiveorders_time < FloatTime() - 5) { + BotHarvesterOrders(bs); + //give orders again after 30 seconds + bs->teamgiveorders_time = FloatTime() + 30; + } + break; + } +#endif + } +} + diff --git a/reaction/engine/code/game/ai_team.h b/reaction/engine/code/game/ai_team.h new file mode 100644 index 00000000..252e9e18 --- /dev/null +++ b/reaction/engine/code/game/ai_team.h @@ -0,0 +1,39 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: ai_team.h + * + * desc: Quake3 bot AI + * + * $Archive: /source/code/botai/ai_chat.c $ + * + *****************************************************************************/ + +void BotTeamAI(bot_state_t *bs); +int BotGetTeamMateTaskPreference(bot_state_t *bs, int teammate); +void BotSetTeamMateTaskPreference(bot_state_t *bs, int teammate, int preference); +void BotVoiceChat(bot_state_t *bs, int toclient, char *voicechat); +void BotVoiceChatOnly(bot_state_t *bs, int toclient, char *voicechat); + + diff --git a/reaction/engine/code/game/ai_vcmd.c b/reaction/engine/code/game/ai_vcmd.c new file mode 100644 index 00000000..205c4b8a --- /dev/null +++ b/reaction/engine/code/game/ai_vcmd.c @@ -0,0 +1,550 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: ai_vcmd.c + * + * desc: Quake3 bot AI + * + * $Archive: /MissionPack/code/game/ai_vcmd.c $ + * + *****************************************************************************/ + +#include "g_local.h" +#include "../botlib/botlib.h" +#include "../botlib/be_aas.h" +#include "../botlib/be_ea.h" +#include "../botlib/be_ai_char.h" +#include "../botlib/be_ai_chat.h" +#include "../botlib/be_ai_gen.h" +#include "../botlib/be_ai_goal.h" +#include "../botlib/be_ai_move.h" +#include "../botlib/be_ai_weap.h" +// +#include "ai_main.h" +#include "ai_dmq3.h" +#include "ai_chat.h" +#include "ai_cmd.h" +#include "ai_dmnet.h" +#include "ai_team.h" +#include "ai_vcmd.h" +// +#include "chars.h" //characteristics +#include "inv.h" //indexes into the inventory +#include "syn.h" //synonyms +#include "match.h" //string matching types and vars + +// for the voice chats +#include "../../ui/menudef.h" + + +typedef struct voiceCommand_s +{ + char *cmd; + void (*func)(bot_state_t *bs, int client, int mode); +} voiceCommand_t; + +/* +================== +BotVoiceChat_GetFlag +================== +*/ +void BotVoiceChat_GetFlag(bot_state_t *bs, int client, int mode) { + // + if (gametype == GT_CTF) { + if (!ctf_redflag.areanum || !ctf_blueflag.areanum) + return; + } +#ifdef MISSIONPACK + else if (gametype == GT_1FCTF) { + if (!ctf_neutralflag.areanum || !ctf_redflag.areanum || !ctf_blueflag.areanum) + return; + } +#endif + else { + return; + } + // + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_GETFLAG; + //set the team goal time + bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME; + // get an alternate route in ctf + if (gametype == GT_CTF) { + //get an alternative route goal towards the enemy base + BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); + } + // + BotSetTeamStatus(bs); + // remember last ordered task + BotRememberLastOrderedTask(bs); +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotVoiceChat_Offense +================== +*/ +void BotVoiceChat_Offense(bot_state_t *bs, int client, int mode) { + if ( gametype == GT_CTF +#ifdef MISSIONPACK + || gametype == GT_1FCTF +#endif + ) { + BotVoiceChat_GetFlag(bs, client, mode); + return; + } +#ifdef MISSIONPACK + if (gametype == GT_HARVESTER) { + // + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_HARVEST; + //set the team goal time + bs->teamgoal_time = FloatTime() + TEAM_HARVEST_TIME; + bs->harvestaway_time = 0; + // + BotSetTeamStatus(bs); + // remember last ordered task + BotRememberLastOrderedTask(bs); + } + else +#endif + { + // + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_ATTACKENEMYBASE; + //set the team goal time + bs->teamgoal_time = FloatTime() + TEAM_ATTACKENEMYBASE_TIME; + bs->attackaway_time = 0; + // + BotSetTeamStatus(bs); + // remember last ordered task + BotRememberLastOrderedTask(bs); + } +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotVoiceChat_Defend +================== +*/ +void BotVoiceChat_Defend(bot_state_t *bs, int client, int mode) { +#ifdef MISSIONPACK + if ( gametype == GT_OBELISK || gametype == GT_HARVESTER) { + // + switch(BotTeam(bs)) { + case TEAM_RED: memcpy(&bs->teamgoal, &redobelisk, sizeof(bot_goal_t)); break; + case TEAM_BLUE: memcpy(&bs->teamgoal, &blueobelisk, sizeof(bot_goal_t)); break; + default: return; + } + } + else +#endif + if (gametype == GT_CTF +#ifdef MISSIONPACK + || gametype == GT_1FCTF +#endif + ) { + // + switch(BotTeam(bs)) { + case TEAM_RED: memcpy(&bs->teamgoal, &ctf_redflag, sizeof(bot_goal_t)); break; + case TEAM_BLUE: memcpy(&bs->teamgoal, &ctf_blueflag, sizeof(bot_goal_t)); break; + default: return; + } + } + else { + return; + } + // + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_DEFENDKEYAREA; + //get the team goal time + bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; + //away from defending + bs->defendaway_time = 0; + // + BotSetTeamStatus(bs); + // remember last ordered task + BotRememberLastOrderedTask(bs); +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotVoiceChat_DefendFlag +================== +*/ +void BotVoiceChat_DefendFlag(bot_state_t *bs, int client, int mode) { + BotVoiceChat_Defend(bs, client, mode); +} + +/* +================== +BotVoiceChat_Patrol +================== +*/ +void BotVoiceChat_Patrol(bot_state_t *bs, int client, int mode) { + // + bs->decisionmaker = client; + // + bs->ltgtype = 0; + bs->lead_time = 0; + bs->lastgoal_ltgtype = 0; + // + BotAI_BotInitialChat(bs, "dismissed", NULL); + trap_BotEnterChat(bs->cs, client, CHAT_TELL); + BotVoiceChatOnly(bs, -1, VOICECHAT_ONPATROL); + // + BotSetTeamStatus(bs); +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotVoiceChat_Camp +================== +*/ +void BotVoiceChat_Camp(bot_state_t *bs, int client, int mode) { + int areanum; + aas_entityinfo_t entinfo; + char netname[MAX_NETNAME]; + + // + 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 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", EasyClientName(client, netname, sizeof(netname)), NULL); + trap_BotEnterChat(bs->cs, client, CHAT_TELL); + return; + } + // + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_CAMPORDER; + //get the team goal time + bs->teamgoal_time = FloatTime() + TEAM_CAMP_TIME; + //the teammate that requested the camping + bs->teammate = client; + //not arrived yet + bs->arrive_time = 0; + // + BotSetTeamStatus(bs); + // remember last ordered task + BotRememberLastOrderedTask(bs); +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotVoiceChat_FollowMe +================== +*/ +void BotVoiceChat_FollowMe(bot_state_t *bs, int client, int mode) { + int areanum; + aas_entityinfo_t entinfo; + char netname[MAX_NETNAME]; + + 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 the other is not visible + if (bs->teamgoal.entitynum < 0) { + BotAI_BotInitialChat(bs, "whereareyou", EasyClientName(client, netname, sizeof(netname)), NULL); + trap_BotEnterChat(bs->cs, client, CHAT_TELL); + return; + } + // + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //the team mate + bs->teammate = client; + //last time the team mate was assumed visible + bs->teammatevisible_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + //get the team goal time + bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; + //set the ltg type + bs->ltgtype = LTG_TEAMACCOMPANY; + bs->formation_dist = 3.5 * 32; //3.5 meter + bs->arrive_time = 0; + // + BotSetTeamStatus(bs); + // remember last ordered task + BotRememberLastOrderedTask(bs); +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotVoiceChat_FollowFlagCarrier +================== +*/ +void BotVoiceChat_FollowFlagCarrier(bot_state_t *bs, int client, int mode) { + int carrier; + + carrier = BotTeamFlagCarrier(bs); + if (carrier >= 0) + BotVoiceChat_FollowMe(bs, carrier, mode); +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotVoiceChat_ReturnFlag +================== +*/ +void BotVoiceChat_ReturnFlag(bot_state_t *bs, int client, int mode) { + //if not in CTF mode + if ( + gametype != GT_CTF +#ifdef MISSIONPACK + && gametype != GT_1FCTF +#endif + ) { + return; + } + // + bs->decisionmaker = client; + bs->ordered = qtrue; + bs->order_time = FloatTime(); + //set the time to send a message to the team mates + bs->teammessage_time = FloatTime() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_RETURNFLAG; + //set the team goal time + bs->teamgoal_time = FloatTime() + CTF_RETURNFLAG_TIME; + bs->rushbaseaway_time = 0; + BotSetTeamStatus(bs); +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +} + +/* +================== +BotVoiceChat_StartLeader +================== +*/ +void BotVoiceChat_StartLeader(bot_state_t *bs, int client, int mode) { + ClientName(client, bs->teamleader, sizeof(bs->teamleader)); +} + +/* +================== +BotVoiceChat_StopLeader +================== +*/ +void BotVoiceChat_StopLeader(bot_state_t *bs, int client, int mode) { + char netname[MAX_MESSAGE_SIZE]; + + if (!Q_stricmp(bs->teamleader, ClientName(client, netname, sizeof(netname)))) { + bs->teamleader[0] = '\0'; + notleader[client] = qtrue; + } +} + +/* +================== +BotVoiceChat_WhoIsLeader +================== +*/ +void BotVoiceChat_WhoIsLeader(bot_state_t *bs, int client, int mode) { + 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)) { + BotAI_BotInitialChat(bs, "iamteamleader", NULL); + trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); + BotVoiceChatOnly(bs, -1, VOICECHAT_STARTLEADER); + } +} + +/* +================== +BotVoiceChat_WantOnDefense +================== +*/ +void BotVoiceChat_WantOnDefense(bot_state_t *bs, int client, int mode) { + char netname[MAX_NETNAME]; + int preference; + + preference = BotGetTeamMateTaskPreference(bs, client); + preference &= ~TEAMTP_ATTACKER; + preference |= TEAMTP_DEFENDER; + BotSetTeamMateTaskPreference(bs, client, preference); + // + EasyClientName(client, netname, sizeof(netname)); + BotAI_BotInitialChat(bs, "keepinmind", netname, NULL); + trap_BotEnterChat(bs->cs, client, CHAT_TELL); + BotVoiceChatOnly(bs, client, VOICECHAT_YES); + trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); +} + +/* +================== +BotVoiceChat_WantOnOffense +================== +*/ +void BotVoiceChat_WantOnOffense(bot_state_t *bs, int client, int mode) { + char netname[MAX_NETNAME]; + int preference; + + preference = BotGetTeamMateTaskPreference(bs, client); + preference &= ~TEAMTP_DEFENDER; + preference |= TEAMTP_ATTACKER; + BotSetTeamMateTaskPreference(bs, client, preference); + // + EasyClientName(client, netname, sizeof(netname)); + BotAI_BotInitialChat(bs, "keepinmind", netname, NULL); + trap_BotEnterChat(bs->cs, client, CHAT_TELL); + BotVoiceChatOnly(bs, client, VOICECHAT_YES); + trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); +} + +void BotVoiceChat_Dummy(bot_state_t *bs, int client, int mode) { +} + +voiceCommand_t voiceCommands[] = { + {VOICECHAT_GETFLAG, BotVoiceChat_GetFlag}, + {VOICECHAT_OFFENSE, BotVoiceChat_Offense }, + {VOICECHAT_DEFEND, BotVoiceChat_Defend }, + {VOICECHAT_DEFENDFLAG, BotVoiceChat_DefendFlag }, + {VOICECHAT_PATROL, BotVoiceChat_Patrol }, + {VOICECHAT_CAMP, BotVoiceChat_Camp }, + {VOICECHAT_FOLLOWME, BotVoiceChat_FollowMe }, + {VOICECHAT_FOLLOWFLAGCARRIER, BotVoiceChat_FollowFlagCarrier }, + {VOICECHAT_RETURNFLAG, BotVoiceChat_ReturnFlag }, + {VOICECHAT_STARTLEADER, BotVoiceChat_StartLeader }, + {VOICECHAT_STOPLEADER, BotVoiceChat_StopLeader }, + {VOICECHAT_WHOISLEADER, BotVoiceChat_WhoIsLeader }, + {VOICECHAT_WANTONDEFENSE, BotVoiceChat_WantOnDefense }, + {VOICECHAT_WANTONOFFENSE, BotVoiceChat_WantOnOffense }, + {NULL, BotVoiceChat_Dummy} +}; + +int BotVoiceChatCommand(bot_state_t *bs, int mode, char *voiceChat) { + int i, voiceOnly, clientNum, color; + char *ptr, buf[MAX_MESSAGE_SIZE], *cmd; + + if (!TeamPlayIsOn()) { + return qfalse; + } + + if ( mode == SAY_ALL ) { + return qfalse; // don't do anything with voice chats to everyone + } + + Q_strncpyz(buf, voiceChat, sizeof(buf)); + cmd = buf; + for (ptr = cmd; *cmd && *cmd > ' '; cmd++); + while (*cmd && *cmd <= ' ') *cmd++ = '\0'; + voiceOnly = atoi(ptr); + for (ptr = cmd; *cmd && *cmd > ' '; cmd++); + while (*cmd && *cmd <= ' ') *cmd++ = '\0'; + clientNum = atoi(ptr); + for (ptr = cmd; *cmd && *cmd > ' '; cmd++); + while (*cmd && *cmd <= ' ') *cmd++ = '\0'; + color = atoi(ptr); + + if (!BotSameTeam(bs, clientNum)) { + return qfalse; + } + + for (i = 0; voiceCommands[i].cmd; i++) { + if (!Q_stricmp(cmd, voiceCommands[i].cmd)) { + voiceCommands[i].func(bs, clientNum, mode); + return qtrue; + } + } + return qfalse; +} diff --git a/reaction/engine/code/game/ai_vcmd.h b/reaction/engine/code/game/ai_vcmd.h new file mode 100644 index 00000000..9a50b142 --- /dev/null +++ b/reaction/engine/code/game/ai_vcmd.h @@ -0,0 +1,36 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: ai_vcmd.h + * + * desc: Quake3 bot AI + * + * $Archive: /source/code/botai/ai_vcmd.c $ + * + *****************************************************************************/ + +int BotVoiceChatCommand(bot_state_t *bs, int mode, char *voicechat); +void BotVoiceChat_Defend(bot_state_t *bs, int client, int mode); + + diff --git a/reaction/engine/code/game/bg_lib.c b/reaction/engine/code/game/bg_lib.c new file mode 100644 index 00000000..a2f5ddb7 --- /dev/null +++ b/reaction/engine/code/game/bg_lib.c @@ -0,0 +1,1793 @@ +// +// +// bg_lib,c -- standard C library replacement routines used by code +// compiled for the virtual machine + +#ifdef Q3_VM + +#include "../qcommon/q_shared.h" + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "bg_lib.h" + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93"; +#endif +static const char rcsid[] = +#endif /* LIBC_SCCS and not lint */ + +static char* med3(char *, char *, char *, cmp_t *); +static void swapfunc(char *, char *, int, int); + +#ifndef min +#define min(a, b) (a) < (b) ? a : b +#endif + +/* + * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". + */ +#define swapcode(TYPE, parmi, parmj, n) { \ + long i = (n) / sizeof (TYPE); \ + register TYPE *pi = (TYPE *) (parmi); \ + register TYPE *pj = (TYPE *) (parmj); \ + do { \ + register TYPE t = *pi; \ + *pi++ = *pj; \ + *pj++ = t; \ + } while (--i > 0); \ +} + +#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \ + es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; + +static void +swapfunc(a, b, n, swaptype) + char *a, *b; + int n, swaptype; +{ + if(swaptype <= 1) + swapcode(long, a, b, n) + else + swapcode(char, a, b, n) +} + +#define swap(a, b) \ + if (swaptype == 0) { \ + long t = *(long *)(a); \ + *(long *)(a) = *(long *)(b); \ + *(long *)(b) = t; \ + } else \ + swapfunc(a, b, es, swaptype) + +#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) + +static char * +med3(a, b, c, cmp) + char *a, *b, *c; + cmp_t *cmp; +{ + return cmp(a, b) < 0 ? + (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a )) + :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c )); +} + +void +qsort(a, n, es, cmp) + void *a; + size_t n, es; + cmp_t *cmp; +{ + char *pa, *pb, *pc, *pd, *pl, *pm, *pn; + int d, r, swaptype, swap_cnt; + +loop: SWAPINIT(a, es); + swap_cnt = 0; + if (n < 7) { + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + pm = (char *)a + (n / 2) * es; + if (n > 7) { + pl = a; + pn = (char *)a + (n - 1) * es; + if (n > 40) { + d = (n / 8) * es; + pl = med3(pl, pl + d, pl + 2 * d, cmp); + pm = med3(pm - d, pm, pm + d, cmp); + pn = med3(pn - 2 * d, pn - d, pn, cmp); + } + pm = med3(pl, pm, pn, cmp); + } + swap(a, pm); + pa = pb = (char *)a + es; + + pc = pd = (char *)a + (n - 1) * es; + for (;;) { + while (pb <= pc && (r = cmp(pb, a)) <= 0) { + if (r == 0) { + swap_cnt = 1; + swap(pa, pb); + pa += es; + } + pb += es; + } + while (pb <= pc && (r = cmp(pc, a)) >= 0) { + if (r == 0) { + swap_cnt = 1; + swap(pc, pd); + pd -= es; + } + pc -= es; + } + if (pb > pc) + break; + swap(pb, pc); + swap_cnt = 1; + pb += es; + pc -= es; + } + if (swap_cnt == 0) { /* Switch to insertion sort */ + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + + pn = (char *)a + n * es; + r = min(pa - (char *)a, pb - pa); + vecswap(a, pb - r, r); + r = min(pd - pc, pn - pd - es); + vecswap(pb, pn - r, r); + if ((r = pb - pa) > es) + qsort(a, r / es, es, cmp); + if ((r = pd - pc) > es) { + /* Iterate rather than recurse to save stack space */ + a = pn - r; + n = r / es; + goto loop; + } +/* qsort(pn - r, r / es, es, cmp);*/ +} + +//================================================================================== + +size_t strlen( const char *string ) { + const char *s; + + s = string; + while ( *s ) { + s++; + } + return s - string; +} + + +char *strcat( char *strDestination, const char *strSource ) { + char *s; + + s = strDestination; + while ( *s ) { + s++; + } + while ( *strSource ) { + *s++ = *strSource++; + } + *s = 0; + return strDestination; +} + +char *strcpy( char *strDestination, const char *strSource ) { + char *s; + + s = strDestination; + while ( *strSource ) { + *s++ = *strSource++; + } + *s = 0; + return strDestination; +} + + +int strcmp( const char *string1, const char *string2 ) { + while ( *string1 == *string2 && *string1 && *string2 ) { + string1++; + string2++; + } + return *string1 - *string2; +} + + +char *strchr( const char *string, int c ) { + while ( *string ) { + if ( *string == c ) { + return ( char * )string; + } + string++; + } + return (char *)0; +} + +char *strstr( const char *string, const char *strCharSet ) { + while ( *string ) { + int i; + + for ( i = 0 ; strCharSet[i] ; i++ ) { + if ( string[i] != strCharSet[i] ) { + break; + } + } + if ( !strCharSet[i] ) { + return (char *)string; + } + string++; + } + return (char *)0; +} + +int tolower( int c ) { + if ( c >= 'A' && c <= 'Z' ) { + c += 'a' - 'A'; + } + return c; +} + + +int toupper( int c ) { + if ( c >= 'a' && c <= 'z' ) { + c += 'A' - 'a'; + } + return c; +} + +void *memmove( void *dest, const void *src, size_t count ) { + int i; + + if ( dest > src ) { + for ( i = count-1 ; i >= 0 ; i-- ) { + ((char *)dest)[i] = ((char *)src)[i]; + } + } else { + for ( i = 0 ; i < count ; i++ ) { + ((char *)dest)[i] = ((char *)src)[i]; + } + } + return dest; +} + + +#if 0 + +double floor( double x ) { + return (int)(x + 0x40000000) - 0x40000000; +} + +void *memset( void *dest, int c, size_t count ) { + while ( count-- ) { + ((char *)dest)[count] = c; + } + return dest; +} + +void *memcpy( void *dest, const void *src, size_t count ) { + while ( count-- ) { + ((char *)dest)[count] = ((char *)src)[count]; + } + return dest; +} + +char *strncpy( char *strDest, const char *strSource, size_t count ) { + char *s; + + s = strDest; + while ( *strSource && count ) { + *s++ = *strSource++; + count--; + } + while ( count-- ) { + *s++ = 0; + } + return strDest; +} + +double sqrt( double x ) { + float y; + float delta; + float maxError; + + if ( x <= 0 ) { + return 0; + } + + // initial guess + y = x / 2; + + // refine + maxError = x * 0.001; + + do { + delta = ( y * y ) - x; + y -= delta / ( 2 * y ); + } while ( delta > maxError || delta < -maxError ); + + return y; +} + + +float sintable[1024] = { +0.000000,0.001534,0.003068,0.004602,0.006136,0.007670,0.009204,0.010738, +0.012272,0.013805,0.015339,0.016873,0.018407,0.019940,0.021474,0.023008, +0.024541,0.026075,0.027608,0.029142,0.030675,0.032208,0.033741,0.035274, +0.036807,0.038340,0.039873,0.041406,0.042938,0.044471,0.046003,0.047535, +0.049068,0.050600,0.052132,0.053664,0.055195,0.056727,0.058258,0.059790, +0.061321,0.062852,0.064383,0.065913,0.067444,0.068974,0.070505,0.072035, +0.073565,0.075094,0.076624,0.078153,0.079682,0.081211,0.082740,0.084269, +0.085797,0.087326,0.088854,0.090381,0.091909,0.093436,0.094963,0.096490, +0.098017,0.099544,0.101070,0.102596,0.104122,0.105647,0.107172,0.108697, +0.110222,0.111747,0.113271,0.114795,0.116319,0.117842,0.119365,0.120888, +0.122411,0.123933,0.125455,0.126977,0.128498,0.130019,0.131540,0.133061, +0.134581,0.136101,0.137620,0.139139,0.140658,0.142177,0.143695,0.145213, +0.146730,0.148248,0.149765,0.151281,0.152797,0.154313,0.155828,0.157343, +0.158858,0.160372,0.161886,0.163400,0.164913,0.166426,0.167938,0.169450, +0.170962,0.172473,0.173984,0.175494,0.177004,0.178514,0.180023,0.181532, +0.183040,0.184548,0.186055,0.187562,0.189069,0.190575,0.192080,0.193586, +0.195090,0.196595,0.198098,0.199602,0.201105,0.202607,0.204109,0.205610, +0.207111,0.208612,0.210112,0.211611,0.213110,0.214609,0.216107,0.217604, +0.219101,0.220598,0.222094,0.223589,0.225084,0.226578,0.228072,0.229565, +0.231058,0.232550,0.234042,0.235533,0.237024,0.238514,0.240003,0.241492, +0.242980,0.244468,0.245955,0.247442,0.248928,0.250413,0.251898,0.253382, +0.254866,0.256349,0.257831,0.259313,0.260794,0.262275,0.263755,0.265234, +0.266713,0.268191,0.269668,0.271145,0.272621,0.274097,0.275572,0.277046, +0.278520,0.279993,0.281465,0.282937,0.284408,0.285878,0.287347,0.288816, +0.290285,0.291752,0.293219,0.294685,0.296151,0.297616,0.299080,0.300543, +0.302006,0.303468,0.304929,0.306390,0.307850,0.309309,0.310767,0.312225, +0.313682,0.315138,0.316593,0.318048,0.319502,0.320955,0.322408,0.323859, +0.325310,0.326760,0.328210,0.329658,0.331106,0.332553,0.334000,0.335445, +0.336890,0.338334,0.339777,0.341219,0.342661,0.344101,0.345541,0.346980, +0.348419,0.349856,0.351293,0.352729,0.354164,0.355598,0.357031,0.358463, +0.359895,0.361326,0.362756,0.364185,0.365613,0.367040,0.368467,0.369892, +0.371317,0.372741,0.374164,0.375586,0.377007,0.378428,0.379847,0.381266, +0.382683,0.384100,0.385516,0.386931,0.388345,0.389758,0.391170,0.392582, +0.393992,0.395401,0.396810,0.398218,0.399624,0.401030,0.402435,0.403838, +0.405241,0.406643,0.408044,0.409444,0.410843,0.412241,0.413638,0.415034, +0.416430,0.417824,0.419217,0.420609,0.422000,0.423390,0.424780,0.426168, +0.427555,0.428941,0.430326,0.431711,0.433094,0.434476,0.435857,0.437237, +0.438616,0.439994,0.441371,0.442747,0.444122,0.445496,0.446869,0.448241, +0.449611,0.450981,0.452350,0.453717,0.455084,0.456449,0.457813,0.459177, +0.460539,0.461900,0.463260,0.464619,0.465976,0.467333,0.468689,0.470043, +0.471397,0.472749,0.474100,0.475450,0.476799,0.478147,0.479494,0.480839, +0.482184,0.483527,0.484869,0.486210,0.487550,0.488889,0.490226,0.491563, +0.492898,0.494232,0.495565,0.496897,0.498228,0.499557,0.500885,0.502212, +0.503538,0.504863,0.506187,0.507509,0.508830,0.510150,0.511469,0.512786, +0.514103,0.515418,0.516732,0.518045,0.519356,0.520666,0.521975,0.523283, +0.524590,0.525895,0.527199,0.528502,0.529804,0.531104,0.532403,0.533701, +0.534998,0.536293,0.537587,0.538880,0.540171,0.541462,0.542751,0.544039, +0.545325,0.546610,0.547894,0.549177,0.550458,0.551738,0.553017,0.554294, +0.555570,0.556845,0.558119,0.559391,0.560662,0.561931,0.563199,0.564466, +0.565732,0.566996,0.568259,0.569521,0.570781,0.572040,0.573297,0.574553, +0.575808,0.577062,0.578314,0.579565,0.580814,0.582062,0.583309,0.584554, +0.585798,0.587040,0.588282,0.589521,0.590760,0.591997,0.593232,0.594466, +0.595699,0.596931,0.598161,0.599389,0.600616,0.601842,0.603067,0.604290, +0.605511,0.606731,0.607950,0.609167,0.610383,0.611597,0.612810,0.614022, +0.615232,0.616440,0.617647,0.618853,0.620057,0.621260,0.622461,0.623661, +0.624859,0.626056,0.627252,0.628446,0.629638,0.630829,0.632019,0.633207, +0.634393,0.635578,0.636762,0.637944,0.639124,0.640303,0.641481,0.642657, +0.643832,0.645005,0.646176,0.647346,0.648514,0.649681,0.650847,0.652011, +0.653173,0.654334,0.655493,0.656651,0.657807,0.658961,0.660114,0.661266, +0.662416,0.663564,0.664711,0.665856,0.667000,0.668142,0.669283,0.670422, +0.671559,0.672695,0.673829,0.674962,0.676093,0.677222,0.678350,0.679476, +0.680601,0.681724,0.682846,0.683965,0.685084,0.686200,0.687315,0.688429, +0.689541,0.690651,0.691759,0.692866,0.693971,0.695075,0.696177,0.697278, +0.698376,0.699473,0.700569,0.701663,0.702755,0.703845,0.704934,0.706021, +0.707107,0.708191,0.709273,0.710353,0.711432,0.712509,0.713585,0.714659, +0.715731,0.716801,0.717870,0.718937,0.720003,0.721066,0.722128,0.723188, +0.724247,0.725304,0.726359,0.727413,0.728464,0.729514,0.730563,0.731609, +0.732654,0.733697,0.734739,0.735779,0.736817,0.737853,0.738887,0.739920, +0.740951,0.741980,0.743008,0.744034,0.745058,0.746080,0.747101,0.748119, +0.749136,0.750152,0.751165,0.752177,0.753187,0.754195,0.755201,0.756206, +0.757209,0.758210,0.759209,0.760207,0.761202,0.762196,0.763188,0.764179, +0.765167,0.766154,0.767139,0.768122,0.769103,0.770083,0.771061,0.772036, +0.773010,0.773983,0.774953,0.775922,0.776888,0.777853,0.778817,0.779778, +0.780737,0.781695,0.782651,0.783605,0.784557,0.785507,0.786455,0.787402, +0.788346,0.789289,0.790230,0.791169,0.792107,0.793042,0.793975,0.794907, +0.795837,0.796765,0.797691,0.798615,0.799537,0.800458,0.801376,0.802293, +0.803208,0.804120,0.805031,0.805940,0.806848,0.807753,0.808656,0.809558, +0.810457,0.811355,0.812251,0.813144,0.814036,0.814926,0.815814,0.816701, +0.817585,0.818467,0.819348,0.820226,0.821103,0.821977,0.822850,0.823721, +0.824589,0.825456,0.826321,0.827184,0.828045,0.828904,0.829761,0.830616, +0.831470,0.832321,0.833170,0.834018,0.834863,0.835706,0.836548,0.837387, +0.838225,0.839060,0.839894,0.840725,0.841555,0.842383,0.843208,0.844032, +0.844854,0.845673,0.846491,0.847307,0.848120,0.848932,0.849742,0.850549, +0.851355,0.852159,0.852961,0.853760,0.854558,0.855354,0.856147,0.856939, +0.857729,0.858516,0.859302,0.860085,0.860867,0.861646,0.862424,0.863199, +0.863973,0.864744,0.865514,0.866281,0.867046,0.867809,0.868571,0.869330, +0.870087,0.870842,0.871595,0.872346,0.873095,0.873842,0.874587,0.875329, +0.876070,0.876809,0.877545,0.878280,0.879012,0.879743,0.880471,0.881197, +0.881921,0.882643,0.883363,0.884081,0.884797,0.885511,0.886223,0.886932, +0.887640,0.888345,0.889048,0.889750,0.890449,0.891146,0.891841,0.892534, +0.893224,0.893913,0.894599,0.895284,0.895966,0.896646,0.897325,0.898001, +0.898674,0.899346,0.900016,0.900683,0.901349,0.902012,0.902673,0.903332, +0.903989,0.904644,0.905297,0.905947,0.906596,0.907242,0.907886,0.908528, +0.909168,0.909806,0.910441,0.911075,0.911706,0.912335,0.912962,0.913587, +0.914210,0.914830,0.915449,0.916065,0.916679,0.917291,0.917901,0.918508, +0.919114,0.919717,0.920318,0.920917,0.921514,0.922109,0.922701,0.923291, +0.923880,0.924465,0.925049,0.925631,0.926210,0.926787,0.927363,0.927935, +0.928506,0.929075,0.929641,0.930205,0.930767,0.931327,0.931884,0.932440, +0.932993,0.933544,0.934093,0.934639,0.935184,0.935726,0.936266,0.936803, +0.937339,0.937872,0.938404,0.938932,0.939459,0.939984,0.940506,0.941026, +0.941544,0.942060,0.942573,0.943084,0.943593,0.944100,0.944605,0.945107, +0.945607,0.946105,0.946601,0.947094,0.947586,0.948075,0.948561,0.949046, +0.949528,0.950008,0.950486,0.950962,0.951435,0.951906,0.952375,0.952842, +0.953306,0.953768,0.954228,0.954686,0.955141,0.955594,0.956045,0.956494, +0.956940,0.957385,0.957826,0.958266,0.958703,0.959139,0.959572,0.960002, +0.960431,0.960857,0.961280,0.961702,0.962121,0.962538,0.962953,0.963366, +0.963776,0.964184,0.964590,0.964993,0.965394,0.965793,0.966190,0.966584, +0.966976,0.967366,0.967754,0.968139,0.968522,0.968903,0.969281,0.969657, +0.970031,0.970403,0.970772,0.971139,0.971504,0.971866,0.972226,0.972584, +0.972940,0.973293,0.973644,0.973993,0.974339,0.974684,0.975025,0.975365, +0.975702,0.976037,0.976370,0.976700,0.977028,0.977354,0.977677,0.977999, +0.978317,0.978634,0.978948,0.979260,0.979570,0.979877,0.980182,0.980485, +0.980785,0.981083,0.981379,0.981673,0.981964,0.982253,0.982539,0.982824, +0.983105,0.983385,0.983662,0.983937,0.984210,0.984480,0.984749,0.985014, +0.985278,0.985539,0.985798,0.986054,0.986308,0.986560,0.986809,0.987057, +0.987301,0.987544,0.987784,0.988022,0.988258,0.988491,0.988722,0.988950, +0.989177,0.989400,0.989622,0.989841,0.990058,0.990273,0.990485,0.990695, +0.990903,0.991108,0.991311,0.991511,0.991710,0.991906,0.992099,0.992291, +0.992480,0.992666,0.992850,0.993032,0.993212,0.993389,0.993564,0.993737, +0.993907,0.994075,0.994240,0.994404,0.994565,0.994723,0.994879,0.995033, +0.995185,0.995334,0.995481,0.995625,0.995767,0.995907,0.996045,0.996180, +0.996313,0.996443,0.996571,0.996697,0.996820,0.996941,0.997060,0.997176, +0.997290,0.997402,0.997511,0.997618,0.997723,0.997825,0.997925,0.998023, +0.998118,0.998211,0.998302,0.998390,0.998476,0.998559,0.998640,0.998719, +0.998795,0.998870,0.998941,0.999011,0.999078,0.999142,0.999205,0.999265, +0.999322,0.999378,0.999431,0.999481,0.999529,0.999575,0.999619,0.999660, +0.999699,0.999735,0.999769,0.999801,0.999831,0.999858,0.999882,0.999905, +0.999925,0.999942,0.999958,0.999971,0.999981,0.999989,0.999995,0.999999 +}; + +double sin( double x ) { + int index; + int quad; + + index = 1024 * x / (M_PI * 0.5); + quad = ( index >> 10 ) & 3; + index &= 1023; + switch ( quad ) { + case 0: + return sintable[index]; + case 1: + return sintable[1023-index]; + case 2: + return -sintable[index]; + case 3: + return -sintable[1023-index]; + } + return 0; +} + + +double cos( double x ) { + int index; + int quad; + + index = 1024 * x / (M_PI * 0.5); + quad = ( index >> 10 ) & 3; + index &= 1023; + switch ( quad ) { + case 3: + return sintable[index]; + case 0: + return sintable[1023-index]; + case 1: + return -sintable[index]; + case 2: + return -sintable[1023-index]; + } + return 0; +} + + +/* +void create_acostable( void ) { + int i; + FILE *fp; + float a; + + fp = fopen("c:\\acostable.txt", "w"); + fprintf(fp, "float acostable[] = {"); + for (i = 0; i < 1024; i++) { + if (!(i & 7)) + fprintf(fp, "\n"); + a = acos( (float) -1 + i / 512 ); + fprintf(fp, "%1.8f,", a); + } + fprintf(fp, "\n}\n"); + fclose(fp); +} +*/ + +float acostable[] = { +3.14159265,3.07908248,3.05317551,3.03328655,3.01651113,3.00172442,2.98834964,2.97604422, +2.96458497,2.95381690,2.94362719,2.93393068,2.92466119,2.91576615,2.90720289,2.89893629, +2.89093699,2.88318015,2.87564455,2.86831188,2.86116621,2.85419358,2.84738169,2.84071962, +2.83419760,2.82780691,2.82153967,2.81538876,2.80934770,2.80341062,2.79757211,2.79182724, +2.78617145,2.78060056,2.77511069,2.76969824,2.76435988,2.75909250,2.75389319,2.74875926, +2.74368816,2.73867752,2.73372510,2.72882880,2.72398665,2.71919677,2.71445741,2.70976688, +2.70512362,2.70052613,2.69597298,2.69146283,2.68699438,2.68256642,2.67817778,2.67382735, +2.66951407,2.66523692,2.66099493,2.65678719,2.65261279,2.64847088,2.64436066,2.64028133, +2.63623214,2.63221238,2.62822133,2.62425835,2.62032277,2.61641398,2.61253138,2.60867440, +2.60484248,2.60103507,2.59725167,2.59349176,2.58975488,2.58604053,2.58234828,2.57867769, +2.57502832,2.57139977,2.56779164,2.56420354,2.56063509,2.55708594,2.55355572,2.55004409, +2.54655073,2.54307530,2.53961750,2.53617701,2.53275354,2.52934680,2.52595650,2.52258238, +2.51922417,2.51588159,2.51255441,2.50924238,2.50594525,2.50266278,2.49939476,2.49614096, +2.49290115,2.48967513,2.48646269,2.48326362,2.48007773,2.47690482,2.47374472,2.47059722, +2.46746215,2.46433933,2.46122860,2.45812977,2.45504269,2.45196720,2.44890314,2.44585034, +2.44280867,2.43977797,2.43675809,2.43374890,2.43075025,2.42776201,2.42478404,2.42181622, +2.41885841,2.41591048,2.41297232,2.41004380,2.40712480,2.40421521,2.40131491,2.39842379, +2.39554173,2.39266863,2.38980439,2.38694889,2.38410204,2.38126374,2.37843388,2.37561237, +2.37279910,2.36999400,2.36719697,2.36440790,2.36162673,2.35885335,2.35608768,2.35332964, +2.35057914,2.34783610,2.34510044,2.34237208,2.33965094,2.33693695,2.33423003,2.33153010, +2.32883709,2.32615093,2.32347155,2.32079888,2.31813284,2.31547337,2.31282041,2.31017388, +2.30753373,2.30489988,2.30227228,2.29965086,2.29703556,2.29442632,2.29182309,2.28922580, +2.28663439,2.28404881,2.28146900,2.27889490,2.27632647,2.27376364,2.27120637,2.26865460, +2.26610827,2.26356735,2.26103177,2.25850149,2.25597646,2.25345663,2.25094195,2.24843238, +2.24592786,2.24342836,2.24093382,2.23844420,2.23595946,2.23347956,2.23100444,2.22853408, +2.22606842,2.22360742,2.22115104,2.21869925,2.21625199,2.21380924,2.21137096,2.20893709, +2.20650761,2.20408248,2.20166166,2.19924511,2.19683280,2.19442469,2.19202074,2.18962092, +2.18722520,2.18483354,2.18244590,2.18006225,2.17768257,2.17530680,2.17293493,2.17056692, +2.16820274,2.16584236,2.16348574,2.16113285,2.15878367,2.15643816,2.15409630,2.15175805, +2.14942338,2.14709226,2.14476468,2.14244059,2.14011997,2.13780279,2.13548903,2.13317865, +2.13087163,2.12856795,2.12626757,2.12397047,2.12167662,2.11938600,2.11709859,2.11481435, +2.11253326,2.11025530,2.10798044,2.10570867,2.10343994,2.10117424,2.09891156,2.09665185, +2.09439510,2.09214129,2.08989040,2.08764239,2.08539725,2.08315496,2.08091550,2.07867884, +2.07644495,2.07421383,2.07198545,2.06975978,2.06753681,2.06531651,2.06309887,2.06088387, +2.05867147,2.05646168,2.05425445,2.05204979,2.04984765,2.04764804,2.04545092,2.04325628, +2.04106409,2.03887435,2.03668703,2.03450211,2.03231957,2.03013941,2.02796159,2.02578610, +2.02361292,2.02144204,2.01927344,2.01710710,2.01494300,2.01278113,2.01062146,2.00846399, +2.00630870,2.00415556,2.00200457,1.99985570,1.99770895,1.99556429,1.99342171,1.99128119, +1.98914271,1.98700627,1.98487185,1.98273942,1.98060898,1.97848051,1.97635399,1.97422942, +1.97210676,1.96998602,1.96786718,1.96575021,1.96363511,1.96152187,1.95941046,1.95730088, +1.95519310,1.95308712,1.95098292,1.94888050,1.94677982,1.94468089,1.94258368,1.94048818, +1.93839439,1.93630228,1.93421185,1.93212308,1.93003595,1.92795046,1.92586659,1.92378433, +1.92170367,1.91962459,1.91754708,1.91547113,1.91339673,1.91132385,1.90925250,1.90718266, +1.90511432,1.90304746,1.90098208,1.89891815,1.89685568,1.89479464,1.89273503,1.89067683, +1.88862003,1.88656463,1.88451060,1.88245794,1.88040664,1.87835668,1.87630806,1.87426076, +1.87221477,1.87017008,1.86812668,1.86608457,1.86404371,1.86200412,1.85996577,1.85792866, +1.85589277,1.85385809,1.85182462,1.84979234,1.84776125,1.84573132,1.84370256,1.84167495, +1.83964848,1.83762314,1.83559892,1.83357582,1.83155381,1.82953289,1.82751305,1.82549429, +1.82347658,1.82145993,1.81944431,1.81742973,1.81541617,1.81340362,1.81139207,1.80938151, +1.80737194,1.80536334,1.80335570,1.80134902,1.79934328,1.79733848,1.79533460,1.79333164, +1.79132959,1.78932843,1.78732817,1.78532878,1.78333027,1.78133261,1.77933581,1.77733985, +1.77534473,1.77335043,1.77135695,1.76936428,1.76737240,1.76538132,1.76339101,1.76140148, +1.75941271,1.75742470,1.75543743,1.75345090,1.75146510,1.74948002,1.74749565,1.74551198, +1.74352900,1.74154672,1.73956511,1.73758417,1.73560389,1.73362426,1.73164527,1.72966692, +1.72768920,1.72571209,1.72373560,1.72175971,1.71978441,1.71780969,1.71583556,1.71386199, +1.71188899,1.70991653,1.70794462,1.70597325,1.70400241,1.70203209,1.70006228,1.69809297, +1.69612416,1.69415584,1.69218799,1.69022062,1.68825372,1.68628727,1.68432127,1.68235571, +1.68039058,1.67842588,1.67646160,1.67449772,1.67253424,1.67057116,1.66860847,1.66664615, +1.66468420,1.66272262,1.66076139,1.65880050,1.65683996,1.65487975,1.65291986,1.65096028, +1.64900102,1.64704205,1.64508338,1.64312500,1.64116689,1.63920905,1.63725148,1.63529416, +1.63333709,1.63138026,1.62942366,1.62746728,1.62551112,1.62355517,1.62159943,1.61964388, +1.61768851,1.61573332,1.61377831,1.61182346,1.60986877,1.60791422,1.60595982,1.60400556, +1.60205142,1.60009739,1.59814349,1.59618968,1.59423597,1.59228235,1.59032882,1.58837536, +1.58642196,1.58446863,1.58251535,1.58056211,1.57860891,1.57665574,1.57470259,1.57274945, +1.57079633,1.56884320,1.56689007,1.56493692,1.56298375,1.56103055,1.55907731,1.55712403, +1.55517069,1.55321730,1.55126383,1.54931030,1.54735668,1.54540297,1.54344917,1.54149526, +1.53954124,1.53758710,1.53563283,1.53367843,1.53172389,1.52976919,1.52781434,1.52585933, +1.52390414,1.52194878,1.51999323,1.51803748,1.51608153,1.51412537,1.51216900,1.51021240, +1.50825556,1.50629849,1.50434117,1.50238360,1.50042576,1.49846765,1.49650927,1.49455060, +1.49259163,1.49063237,1.48867280,1.48671291,1.48475270,1.48279215,1.48083127,1.47887004, +1.47690845,1.47494650,1.47298419,1.47102149,1.46905841,1.46709493,1.46513106,1.46316677, +1.46120207,1.45923694,1.45727138,1.45530538,1.45333893,1.45137203,1.44940466,1.44743682, +1.44546850,1.44349969,1.44153038,1.43956057,1.43759024,1.43561940,1.43364803,1.43167612, +1.42970367,1.42773066,1.42575709,1.42378296,1.42180825,1.41983295,1.41785705,1.41588056, +1.41390346,1.41192573,1.40994738,1.40796840,1.40598877,1.40400849,1.40202755,1.40004594, +1.39806365,1.39608068,1.39409701,1.39211264,1.39012756,1.38814175,1.38615522,1.38416795, +1.38217994,1.38019117,1.37820164,1.37621134,1.37422025,1.37222837,1.37023570,1.36824222, +1.36624792,1.36425280,1.36225684,1.36026004,1.35826239,1.35626387,1.35426449,1.35226422, +1.35026307,1.34826101,1.34625805,1.34425418,1.34224937,1.34024364,1.33823695,1.33622932, +1.33422072,1.33221114,1.33020059,1.32818904,1.32617649,1.32416292,1.32214834,1.32013273, +1.31811607,1.31609837,1.31407960,1.31205976,1.31003885,1.30801684,1.30599373,1.30396951, +1.30194417,1.29991770,1.29789009,1.29586133,1.29383141,1.29180031,1.28976803,1.28773456, +1.28569989,1.28366400,1.28162688,1.27958854,1.27754894,1.27550809,1.27346597,1.27142257, +1.26937788,1.26733189,1.26528459,1.26323597,1.26118602,1.25913471,1.25708205,1.25502803, +1.25297262,1.25091583,1.24885763,1.24679802,1.24473698,1.24267450,1.24061058,1.23854519, +1.23647833,1.23440999,1.23234015,1.23026880,1.22819593,1.22612152,1.22404557,1.22196806, +1.21988898,1.21780832,1.21572606,1.21364219,1.21155670,1.20946958,1.20738080,1.20529037, +1.20319826,1.20110447,1.19900898,1.19691177,1.19481283,1.19271216,1.19060973,1.18850553, +1.18639955,1.18429178,1.18218219,1.18007079,1.17795754,1.17584244,1.17372548,1.17160663, +1.16948589,1.16736324,1.16523866,1.16311215,1.16098368,1.15885323,1.15672081,1.15458638, +1.15244994,1.15031147,1.14817095,1.14602836,1.14388370,1.14173695,1.13958808,1.13743709, +1.13528396,1.13312866,1.13097119,1.12881153,1.12664966,1.12448556,1.12231921,1.12015061, +1.11797973,1.11580656,1.11363107,1.11145325,1.10927308,1.10709055,1.10490563,1.10271831, +1.10052856,1.09833638,1.09614174,1.09394462,1.09174500,1.08954287,1.08733820,1.08513098, +1.08292118,1.08070879,1.07849378,1.07627614,1.07405585,1.07183287,1.06960721,1.06737882, +1.06514770,1.06291382,1.06067715,1.05843769,1.05619540,1.05395026,1.05170226,1.04945136, +1.04719755,1.04494080,1.04268110,1.04041841,1.03815271,1.03588399,1.03361221,1.03133735, +1.02905939,1.02677830,1.02449407,1.02220665,1.01991603,1.01762219,1.01532509,1.01302471, +1.01072102,1.00841400,1.00610363,1.00378986,1.00147268,0.99915206,0.99682798,0.99450039, +0.99216928,0.98983461,0.98749636,0.98515449,0.98280898,0.98045980,0.97810691,0.97575030, +0.97338991,0.97102573,0.96865772,0.96628585,0.96391009,0.96153040,0.95914675,0.95675912, +0.95436745,0.95197173,0.94957191,0.94716796,0.94475985,0.94234754,0.93993099,0.93751017, +0.93508504,0.93265556,0.93022170,0.92778341,0.92534066,0.92289341,0.92044161,0.91798524, +0.91552424,0.91305858,0.91058821,0.90811309,0.90563319,0.90314845,0.90065884,0.89816430, +0.89566479,0.89316028,0.89065070,0.88813602,0.88561619,0.88309116,0.88056088,0.87802531, +0.87548438,0.87293806,0.87038629,0.86782901,0.86526619,0.86269775,0.86012366,0.85754385, +0.85495827,0.85236686,0.84976956,0.84716633,0.84455709,0.84194179,0.83932037,0.83669277, +0.83405893,0.83141877,0.82877225,0.82611928,0.82345981,0.82079378,0.81812110,0.81544172, +0.81275556,0.81006255,0.80736262,0.80465570,0.80194171,0.79922057,0.79649221,0.79375655, +0.79101352,0.78826302,0.78550497,0.78273931,0.77996593,0.77718475,0.77439569,0.77159865, +0.76879355,0.76598029,0.76315878,0.76032891,0.75749061,0.75464376,0.75178826,0.74892402, +0.74605092,0.74316887,0.74027775,0.73737744,0.73446785,0.73154885,0.72862033,0.72568217, +0.72273425,0.71977644,0.71680861,0.71383064,0.71084240,0.70784376,0.70483456,0.70181469, +0.69878398,0.69574231,0.69268952,0.68962545,0.68654996,0.68346288,0.68036406,0.67725332, +0.67413051,0.67099544,0.66784794,0.66468783,0.66151492,0.65832903,0.65512997,0.65191753, +0.64869151,0.64545170,0.64219789,0.63892987,0.63564741,0.63235028,0.62903824,0.62571106, +0.62236849,0.61901027,0.61563615,0.61224585,0.60883911,0.60541564,0.60197515,0.59851735, +0.59504192,0.59154856,0.58803694,0.58450672,0.58095756,0.57738911,0.57380101,0.57019288, +0.56656433,0.56291496,0.55924437,0.55555212,0.55183778,0.54810089,0.54434099,0.54055758, +0.53675018,0.53291825,0.52906127,0.52517867,0.52126988,0.51733431,0.51337132,0.50938028, +0.50536051,0.50131132,0.49723200,0.49312177,0.48897987,0.48480547,0.48059772,0.47635573, +0.47207859,0.46776530,0.46341487,0.45902623,0.45459827,0.45012983,0.44561967,0.44106652, +0.43646903,0.43182577,0.42713525,0.42239588,0.41760600,0.41276385,0.40786755,0.40291513, +0.39790449,0.39283339,0.38769946,0.38250016,0.37723277,0.37189441,0.36648196,0.36099209, +0.35542120,0.34976542,0.34402054,0.33818204,0.33224495,0.32620390,0.32005298,0.31378574, +0.30739505,0.30087304,0.29421096,0.28739907,0.28042645,0.27328078,0.26594810,0.25841250, +0.25065566,0.24265636,0.23438976,0.22582651,0.21693146,0.20766198,0.19796546,0.18777575, +0.17700769,0.16554844,0.15324301,0.13986823,0.12508152,0.10830610,0.08841715,0.06251018, +} + +double acos( double x ) { + int index; + + if (x < -1) + x = -1; + if (x > 1) + x = 1; + index = (float) (1.0 + x) * 511.9; + return acostable[index]; +} + +double atan2( double y, double x ) { + float base; + float temp; + float dir; + float test; + int i; + + if ( x < 0 ) { + if ( y >= 0 ) { + // quad 1 + base = M_PI / 2; + temp = x; + x = y; + y = -temp; + } else { + // quad 2 + base = M_PI; + x = -x; + y = -y; + } + } else { + if ( y < 0 ) { + // quad 3 + base = 3 * M_PI / 2; + temp = x; + x = -y; + y = temp; + } + } + + if ( y > x ) { + base += M_PI/2; + temp = x; + x = y; + y = temp; + dir = -1; + } else { + dir = 1; + } + + // calcualte angle in octant 0 + if ( x == 0 ) { + return base; + } + y /= x; + + for ( i = 0 ; i < 512 ; i++ ) { + test = sintable[i] / sintable[1023-i]; + if ( test > y ) { + break; + } + } + + return base + dir * i * ( M_PI/2048); +} + + +#endif + +double tan( double x ) { + return sin(x) / cos(x); +} + + +static int randSeed = 0; + +void srand( unsigned seed ) { + randSeed = seed; +} + +int rand( void ) { + randSeed = (69069 * randSeed + 1); + return randSeed & 0x7fff; +} + +double atof( const char *string ) { + float sign; + float value; + int c; + + + // skip whitespace + while ( *string <= ' ' ) { + if ( !*string ) { + return 0; + } + string++; + } + + // check sign + switch ( *string ) { + case '+': + string++; + sign = 1; + break; + case '-': + string++; + sign = -1; + break; + default: + sign = 1; + break; + } + + // read digits + value = 0; + c = string[0]; + if ( c != '.' ) { + do { + c = *string++; + if ( c < '0' || c > '9' ) { + break; + } + c -= '0'; + value = value * 10 + c; + } while ( 1 ); + } else { + string++; + } + + // check for decimal point + if ( c == '.' ) { + double fraction; + + fraction = 0.1; + do { + c = *string++; + if ( c < '0' || c > '9' ) { + break; + } + c -= '0'; + value += c * fraction; + fraction *= 0.1; + } while ( 1 ); + + } + + // not handling 10e10 notation... + + return value * sign; +} + +double _atof( const char **stringPtr ) { + const char *string; + float sign; + float value; + int c = '0'; + + string = *stringPtr; + + // skip whitespace + while ( *string <= ' ' ) { + if ( !*string ) { + *stringPtr = string; + return 0; + } + string++; + } + + // check sign + switch ( *string ) { + case '+': + string++; + sign = 1; + break; + case '-': + string++; + sign = -1; + break; + default: + sign = 1; + break; + } + + // read digits + value = 0; + if ( string[0] != '.' ) { + do { + c = *string++; + if ( c < '0' || c > '9' ) { + break; + } + c -= '0'; + value = value * 10 + c; + } while ( 1 ); + } + + // check for decimal point + if ( c == '.' ) { + double fraction; + + fraction = 0.1; + do { + c = *string++; + if ( c < '0' || c > '9' ) { + break; + } + c -= '0'; + value += c * fraction; + fraction *= 0.1; + } while ( 1 ); + + } + + // not handling 10e10 notation... + *stringPtr = string; + + return value * sign; +} + + +int atoi( const char *string ) { + int sign; + int value; + int c; + + + // skip whitespace + while ( *string <= ' ' ) { + if ( !*string ) { + return 0; + } + string++; + } + + // check sign + switch ( *string ) { + case '+': + string++; + sign = 1; + break; + case '-': + string++; + sign = -1; + break; + default: + sign = 1; + break; + } + + // read digits + value = 0; + do { + c = *string++; + if ( c < '0' || c > '9' ) { + break; + } + c -= '0'; + value = value * 10 + c; + } while ( 1 ); + + // not handling 10e10 notation... + + return value * sign; +} + + +int _atoi( const char **stringPtr ) { + int sign; + int value; + int c; + const char *string; + + string = *stringPtr; + + // skip whitespace + while ( *string <= ' ' ) { + if ( !*string ) { + return 0; + } + string++; + } + + // check sign + switch ( *string ) { + case '+': + string++; + sign = 1; + break; + case '-': + string++; + sign = -1; + break; + default: + sign = 1; + break; + } + + // read digits + value = 0; + do { + c = *string++; + if ( c < '0' || c > '9' ) { + break; + } + c -= '0'; + value = value * 10 + c; + } while ( 1 ); + + // not handling 10e10 notation... + + *stringPtr = string; + + return value * sign; +} + +int abs( int n ) { + return n < 0 ? -n : n; +} + +double fabs( double x ) { + return x < 0 ? -x : x; +} + + + +//========================================================= + +/* + * New implementation by Patrick Powell and others for vsnprintf. + * Supports length checking in strings. +*/ + +/* + * Copyright Patrick Powell 1995 + * This code is based on code written by Patrick Powell (papowell@astart.com) + * It may be used for any purpose as long as this notice remains intact + * on all source code distributions + */ + +/************************************************************** + * Original: + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point... + * + * snprintf() is used instead of sprintf() as it does limit checks + * for string length. This covers a nasty loophole. + * + * The other functions are there to prevent NULL pointers from + * causing nast effects. + * + * More Recently: + * Brandon Long 9/15/96 for mutt 0.43 + * This was ugly. It is still ugly. I opted out of floating point + * numbers, but the formatter understands just about everything + * from the normal C string format, at least as far as I can tell from + * the Solaris 2.5 printf(3S) man page. + * + * Brandon Long 10/22/97 for mutt 0.87.1 + * Ok, added some minimal floating point support, which means this + * probably requires libm on most operating systems. Don't yet + * support the exponent (e,E) and sigfig (g,G). Also, fmtint() + * was pretty badly broken, it just wasn't being exercised in ways + * which showed it, so that's been fixed. Also, formated the code + * to mutt conventions, and removed dead code left over from the + * original. Also, there is now a builtin-test, just compile with: + * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm + * and run snprintf for results. + * + * Thomas Roessler 01/27/98 for mutt 0.89i + * The PGP code was using unsigned hexadecimal formats. + * Unfortunately, unsigned formats simply didn't work. + * + * Michael Elkins 03/05/98 for mutt 0.90.8 + * The original code assumed that both snprintf() and vsnprintf() were + * missing. Some systems only have snprintf() but not vsnprintf(), so + * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. + * + * Andrew Tridgell (tridge@samba.org) Oct 1998 + * fixed handling of %.0f + * added test for HAVE_LONG_DOUBLE + * + * Russ Allbery 2000-08-26 + * fixed return value to comply with C99 + * fixed handling of snprintf(NULL, ...) + * + * Hrvoje Niksic 2000-11-04 + * include instead of "config.h". + * moved TEST_SNPRINTF stuff out of HAVE_SNPRINTF ifdef. + * include for NULL. + * added support and test cases for long long. + * don't declare argument types to (v)snprintf if stdarg is not used. + * use int instead of short int as 2nd arg to va_arg. + * + **************************************************************/ + +/* BDR 2002-01-13 %e and %g were being ignored. Now do something, + if not necessarily correctly */ + +#if (SIZEOF_LONG_DOUBLE > 0) +/* #ifdef HAVE_LONG_DOUBLE */ +#define LDOUBLE long double +#else +#define LDOUBLE double +#endif + +#if (SIZEOF_LONG_LONG > 0) +/* #ifdef HAVE_LONG_LONG */ +# define LLONG long long +#else +# define LLONG long +#endif + +static int dopr (char *buffer, size_t maxlen, const char *format, + va_list args); +static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max); +static int fmtint (char *buffer, size_t *currlen, size_t maxlen, + LLONG value, int base, int min, int max, int flags); +static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, + LDOUBLE fvalue, int min, int max, int flags); +static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c ); + +/* + * dopr(): poor man's version of doprintf + */ + +/* format read states */ +#define DP_S_DEFAULT 0 +#define DP_S_FLAGS 1 +#define DP_S_MIN 2 +#define DP_S_DOT 3 +#define DP_S_MAX 4 +#define DP_S_MOD 5 +#define DP_S_MOD_L 6 +#define DP_S_CONV 7 +#define DP_S_DONE 8 + +/* format flags - Bits */ +#define DP_F_MINUS (1 << 0) +#define DP_F_PLUS (1 << 1) +#define DP_F_SPACE (1 << 2) +#define DP_F_NUM (1 << 3) +#define DP_F_ZERO (1 << 4) +#define DP_F_UP (1 << 5) +#define DP_F_UNSIGNED (1 << 6) + +/* Conversion Flags */ +#define DP_C_SHORT 1 +#define DP_C_LONG 2 +#define DP_C_LLONG 3 +#define DP_C_LDOUBLE 4 + +#define char_to_int(p) (p - '0') +#define MAX(p,q) ((p >= q) ? p : q) +#define MIN(p,q) ((p <= q) ? p : q) + +static int dopr (char *buffer, size_t maxlen, const char *format, va_list args) +{ + char ch; + LLONG value; + LDOUBLE fvalue; + char *strvalue; + int min; + int max; + int state; + int flags; + int cflags; + int total; + size_t currlen; + + state = DP_S_DEFAULT; + currlen = flags = cflags = min = 0; + max = -1; + ch = *format++; + total = 0; + + while (state != DP_S_DONE) + { + if (ch == '\0') + state = DP_S_DONE; + + switch(state) + { + case DP_S_DEFAULT: + if (ch == '%') + state = DP_S_FLAGS; + else + total += dopr_outch (buffer, &currlen, maxlen, ch); + ch = *format++; + break; + case DP_S_FLAGS: + switch (ch) + { + case '-': + flags |= DP_F_MINUS; + ch = *format++; + break; + case '+': + flags |= DP_F_PLUS; + ch = *format++; + break; + case ' ': + flags |= DP_F_SPACE; + ch = *format++; + break; + case '#': + flags |= DP_F_NUM; + ch = *format++; + break; + case '0': + flags |= DP_F_ZERO; + ch = *format++; + break; + default: + state = DP_S_MIN; + break; + } + break; + case DP_S_MIN: + if ('0' <= ch && ch <= '9') + { + min = 10*min + char_to_int (ch); + ch = *format++; + } + else if (ch == '*') + { + min = va_arg (args, int); + ch = *format++; + state = DP_S_DOT; + } + else + state = DP_S_DOT; + break; + case DP_S_DOT: + if (ch == '.') + { + state = DP_S_MAX; + ch = *format++; + } + else + state = DP_S_MOD; + break; + case DP_S_MAX: + if ('0' <= ch && ch <= '9') + { + if (max < 0) + max = 0; + max = 10*max + char_to_int (ch); + ch = *format++; + } + else if (ch == '*') + { + max = va_arg (args, int); + ch = *format++; + state = DP_S_MOD; + } + else + state = DP_S_MOD; + break; + case DP_S_MOD: + switch (ch) + { + case 'h': + cflags = DP_C_SHORT; + ch = *format++; + break; + case 'l': + cflags = DP_C_LONG; + ch = *format++; + break; + case 'L': + cflags = DP_C_LDOUBLE; + ch = *format++; + break; + default: + break; + } + if (cflags != DP_C_LONG) + state = DP_S_CONV; + else + state = DP_S_MOD_L; + break; + case DP_S_MOD_L: + switch (ch) + { + case 'l': + cflags = DP_C_LLONG; + ch = *format++; + break; + default: + break; + } + state = DP_S_CONV; + break; + case DP_S_CONV: + switch (ch) + { + case 'd': + case 'i': + if (cflags == DP_C_SHORT) + value = (short int)va_arg (args, int); + else if (cflags == DP_C_LONG) + value = va_arg (args, long int); + else if (cflags == DP_C_LLONG) + value = va_arg (args, LLONG); + else + value = va_arg (args, int); + total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + break; + case 'o': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) +// value = (unsigned short int) va_arg (args, unsigned short int); // Thilo: This does not work because the rcc compiler cannot do that cast correctly. + value = va_arg (args, unsigned int) & ( (1 << sizeof(unsigned short int) * 8) - 1); // Using this workaround instead. + else if (cflags == DP_C_LONG) + value = va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = va_arg (args, unsigned LLONG); + else + value = va_arg (args, unsigned int); + total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); + break; + case 'u': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = va_arg (args, unsigned int) & ( (1 << sizeof(unsigned short int) * 8) - 1); + else if (cflags == DP_C_LONG) + value = va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = va_arg (args, unsigned LLONG); + else + value = va_arg (args, unsigned int); + total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + break; + case 'X': + flags |= DP_F_UP; + case 'x': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = va_arg (args, unsigned int) & ( (1 << sizeof(unsigned short int) * 8) - 1); + else if (cflags == DP_C_LONG) + value = va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = va_arg (args, unsigned LLONG); + else + value = va_arg (args, unsigned int); + total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); + break; + case 'f': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + /* um, floating point? */ + total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); + break; + case 'E': + flags |= DP_F_UP; + case 'e': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + /* um, floating point? */ + total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); + break; + case 'G': + flags |= DP_F_UP; + case 'g': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + /* um, floating point? */ + total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); + break; + case 'c': + total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); + break; + case 's': + strvalue = va_arg (args, char *); + total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); + break; + case 'p': + strvalue = va_arg (args, void *); + total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, + max, flags); + break; + case 'n': + if (cflags == DP_C_SHORT) + { + short int *num; + num = va_arg (args, short int *); + *num = currlen; + } + else if (cflags == DP_C_LONG) + { + long int *num; + num = va_arg (args, long int *); + *num = currlen; + } + else if (cflags == DP_C_LLONG) + { + LLONG *num; + num = va_arg (args, LLONG *); + *num = currlen; + } + else + { + int *num; + num = va_arg (args, int *); + *num = currlen; + } + break; + case '%': + total += dopr_outch (buffer, &currlen, maxlen, ch); + break; + case 'w': + /* not supported yet, treat as next char */ + ch = *format++; + break; + default: + /* Unknown, skip */ + break; + } + ch = *format++; + state = DP_S_DEFAULT; + flags = cflags = min = 0; + max = -1; + break; + case DP_S_DONE: + break; + default: + /* hmm? */ + break; /* some picky compilers need this */ + } + } + if (buffer != NULL) + { + if (currlen < maxlen - 1) + buffer[currlen] = '\0'; + else + buffer[maxlen - 1] = '\0'; + } + return total; +} + +static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max) +{ + int padlen, strln; /* amount to pad */ + int cnt = 0; + int total = 0; + + if (value == 0) + { + value = ""; + } + + for (strln = 0; value[strln]; ++strln); /* strlen */ + if (max >= 0 && max < strln) + strln = max; + padlen = min - strln; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justify */ + + while (padlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, ' '); + --padlen; + } + while (*value && ((max < 0) || (cnt < max))) + { + total += dopr_outch (buffer, currlen, maxlen, *value++); + ++cnt; + } + while (padlen < 0) + { + total += dopr_outch (buffer, currlen, maxlen, ' '); + ++padlen; + } + return total; +} + +/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ + +static int fmtint (char *buffer, size_t *currlen, size_t maxlen, + LLONG value, int base, int min, int max, int flags) +{ + int signvalue = 0; + unsigned LLONG uvalue; + char convert[24]; + int place = 0; + int spadlen = 0; /* amount to space pad */ + int zpadlen = 0; /* amount to zero pad */ + const char *digits; + int total = 0; + + if (max < 0) + max = 0; + + uvalue = value; + + if(!(flags & DP_F_UNSIGNED)) + { + if( value < 0 ) { + signvalue = '-'; + uvalue = -value; + } + else + if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else + if (flags & DP_F_SPACE) + signvalue = ' '; + } + + if (flags & DP_F_UP) + /* Should characters be upper case? */ + digits = "0123456789ABCDEF"; + else + digits = "0123456789abcdef"; + + do { + convert[place++] = digits[uvalue % (unsigned)base]; + uvalue = (uvalue / (unsigned)base ); + } while(uvalue && (place < sizeof (convert))); + if (place == sizeof (convert)) place--; + convert[place] = 0; + + zpadlen = max - place; + spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); + if (zpadlen < 0) zpadlen = 0; + if (spadlen < 0) spadlen = 0; + if (flags & DP_F_ZERO) + { + zpadlen = MAX(zpadlen, spadlen); + spadlen = 0; + } + if (flags & DP_F_MINUS) + spadlen = -spadlen; /* Left Justifty */ + +#ifdef DEBUG_SNPRINTF + dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", + zpadlen, spadlen, min, max, place)); +#endif + + /* Spaces */ + while (spadlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, ' '); + --spadlen; + } + + /* Sign */ + if (signvalue) + total += dopr_outch (buffer, currlen, maxlen, signvalue); + + /* Zeros */ + if (zpadlen > 0) + { + while (zpadlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, '0'); + --zpadlen; + } + } + + /* Digits */ + while (place > 0) + total += dopr_outch (buffer, currlen, maxlen, convert[--place]); + + /* Left Justified spaces */ + while (spadlen < 0) { + total += dopr_outch (buffer, currlen, maxlen, ' '); + ++spadlen; + } + + return total; +} + +static LDOUBLE abs_val (LDOUBLE value) +{ + LDOUBLE result = value; + + if (value < 0) + result = -value; + + return result; +} + +static LDOUBLE pow10 (int exp) +{ + LDOUBLE result = 1; + + while (exp) + { + result *= 10; + exp--; + } + + return result; +} + +static long round (LDOUBLE value) +{ + long intpart; + + intpart = value; + value = value - intpart; + if (value >= 0.5) + intpart++; + + return intpart; +} + +static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, + LDOUBLE fvalue, int min, int max, int flags) +{ + int signvalue = 0; + LDOUBLE ufvalue; + char iconvert[20]; + char fconvert[20]; + int iplace = 0; + int fplace = 0; + int padlen = 0; /* amount to pad */ + int zpadlen = 0; + int caps = 0; + int total = 0; + long intpart; + long fracpart; + + /* + * AIX manpage says the default is 0, but Solaris says the default + * is 6, and sprintf on AIX defaults to 6 + */ + if (max < 0) + max = 6; + + ufvalue = abs_val (fvalue); + + if (fvalue < 0) + signvalue = '-'; + else + if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else + if (flags & DP_F_SPACE) + signvalue = ' '; + +#if 0 + if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ +#endif + + intpart = ufvalue; + + /* + * Sorry, we only support 9 digits past the decimal because of our + * conversion method + */ + if (max > 9) + max = 9; + + /* We "cheat" by converting the fractional part to integer by + * multiplying by a factor of 10 + */ + fracpart = round ((pow10 (max)) * (ufvalue - intpart)); + + if (fracpart >= pow10 (max)) + { + intpart++; + fracpart -= pow10 (max); + } + +#ifdef DEBUG_SNPRINTF + dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart)); +#endif + + /* Convert integer part */ + do { + iconvert[iplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10]; + intpart = (intpart / 10); + } while(intpart && (iplace < 20)); + if (iplace == 20) iplace--; + iconvert[iplace] = 0; + + /* Convert fractional part */ + do { + fconvert[fplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10]; + fracpart = (fracpart / 10); + } while(fracpart && (fplace < 20)); + if (fplace == 20) fplace--; + fconvert[fplace] = 0; + + /* -1 for decimal point, another -1 if we are printing a sign */ + padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); + zpadlen = max - fplace; + if (zpadlen < 0) + zpadlen = 0; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justifty */ + + if ((flags & DP_F_ZERO) && (padlen > 0)) + { + if (signvalue) + { + total += dopr_outch (buffer, currlen, maxlen, signvalue); + --padlen; + signvalue = 0; + } + while (padlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, '0'); + --padlen; + } + } + while (padlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, ' '); + --padlen; + } + if (signvalue) + total += dopr_outch (buffer, currlen, maxlen, signvalue); + + while (iplace > 0) + total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); + + /* + * Decimal point. This should probably use locale to find the correct + * char to print out. + */ + if (max > 0) + { + total += dopr_outch (buffer, currlen, maxlen, '.'); + + while (zpadlen-- > 0) + total += dopr_outch (buffer, currlen, maxlen, '0'); + + while (fplace > 0) + total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); + } + + while (padlen < 0) + { + total += dopr_outch (buffer, currlen, maxlen, ' '); + ++padlen; + } + + return total; +} + +static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) +{ + if (*currlen + 1 < maxlen) + buffer[(*currlen)++] = c; + return 1; +} + +int Q_vsnprintf(char *str, size_t length, const char *fmt, va_list args) +{ + if (str != NULL) + str[0] = 0; + return dopr(str, length, fmt, args); +} + +int Q_snprintf(char *str, size_t length, const char *fmt, ...) +{ + va_list ap; + int retval; + + va_start(ap, fmt); + retval = Q_vsnprintf(str, length, fmt, ap); + va_end(ap); + + return retval; +} + +/* this is really crappy */ +int sscanf( const char *buffer, const char *fmt, ... ) { + int cmd; + int **arg; + int count; + + arg = (int **)&fmt + 1; + count = 0; + + while ( *fmt ) { + if ( fmt[0] != '%' ) { + fmt++; + continue; + } + + cmd = fmt[1]; + fmt += 2; + + switch ( cmd ) { + case 'i': + case 'd': + case 'u': + **arg = _atoi( &buffer ); + break; + case 'f': + *(float *)*arg = _atof( &buffer ); + break; + } + arg++; + } + + return count; +} + +#endif diff --git a/reaction/engine/code/game/bg_lib.h b/reaction/engine/code/game/bg_lib.h new file mode 100644 index 00000000..54a379bd --- /dev/null +++ b/reaction/engine/code/game/bg_lib.h @@ -0,0 +1,123 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// bg_lib.h -- standard C library replacement routines used by code +// compiled for the virtual machine + +// This file is NOT included on native builds +#if !defined( BG_LIB_H ) && defined( Q3_VM ) +#define BG_LIB_H + +//Ignore __attribute__ on non-gcc platforms +#ifndef __GNUC__ +#ifndef __attribute__ +#define __attribute__(x) +#endif +#endif + +#ifndef NULL +#define NULL ((void *)0) +#endif + +typedef int size_t; + +typedef char * va_list; +#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) +#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) +#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) +#define va_end(ap) ( ap = (va_list)0 ) + +#define CHAR_BIT 8 /* number of bits in a char */ +#define SCHAR_MIN (-128) /* minimum signed char value */ +#define SCHAR_MAX 127 /* maximum signed char value */ +#define UCHAR_MAX 0xff /* maximum unsigned char value */ + +#define SHRT_MIN (-32768) /* minimum (signed) short value */ +#define SHRT_MAX 32767 /* maximum (signed) short value */ +#define USHRT_MAX 0xffff /* maximum unsigned short value */ +#define INT_MIN (-2147483647 - 1) /* minimum (signed) int value */ +#define INT_MAX 2147483647 /* maximum (signed) int value */ +#define UINT_MAX 0xffffffff /* maximum unsigned int value */ +#define LONG_MIN (-2147483647L - 1) /* minimum (signed) long value */ +#define LONG_MAX 2147483647L /* maximum (signed) long value */ +#define ULONG_MAX 0xffffffffUL /* maximum unsigned long value */ + +#define isalnum(c) (isalpha(c) || isdigit(c)) +#define isalpha(c) (isupper(c) || islower(c)) +#define isascii(c) ((c) > 0 && (c) <= 0x7f) +#define iscntrl(c) (((c) >= 0) && (((c) <= 0x1f) || ((c) == 0x7f))) +#define isdigit(c) ((c) >= '0' && (c) <= '9') +#define isgraph(c) ((c) != ' ' && isprint(c)) +#define islower(c) ((c) >= 'a' && (c) <= 'z') +#define isprint(c) ((c) >= ' ' && (c) <= '~') +#define ispunct(c) (((c) > ' ' && (c) <= '~') && !isalnum(c)) +#define isspace(c) ((c) == ' ' || (c) == '\f' || (c) == '\n' || (c) == '\r' || \ + (c) == '\t' || (c) == '\v') +#define isupper(c) ((c) >= 'A' && (c) <= 'Z') +#define isxdigit(c) (isxupper(c) || isxlower(c)) +#define isxlower(c) (isdigit(c) || (c >= 'a' && c <= 'f')) +#define isxupper(c) (isdigit(c) || (c >= 'A' && c <= 'F')) + +// Misc functions +typedef int cmp_t(const void *, const void *); +void qsort(void *a, size_t n, size_t es, cmp_t *cmp); +void srand( unsigned seed ); +int rand( void ); + +// String functions +size_t strlen( const char *string ); +char *strcat( char *strDestination, const char *strSource ); +char *strcpy( char *strDestination, const char *strSource ); +int strcmp( const char *string1, const char *string2 ); +char *strchr( const char *string, int c ); +char *strstr( const char *string, const char *strCharSet ); +char *strncpy( char *strDest, const char *strSource, size_t count ); +int tolower( int c ); +int toupper( int c ); + +double atof( const char *string ); +double _atof( const char **stringPtr ); +int atoi( const char *string ); +int _atoi( const char **stringPtr ); + +int Q_vsnprintf( char *buffer, size_t length, const char *fmt, va_list argptr ); +int Q_snprintf( char *buffer, size_t length, const char *fmt, ... ) __attribute__ ((format (printf, 3, 4))); + +int sscanf( const char *buffer, const char *fmt, ... ) __attribute__ ((format (scanf, 2, 3))); + +// Memory functions +void *memmove( void *dest, const void *src, size_t count ); +void *memset( void *dest, int c, size_t count ); +void *memcpy( void *dest, const void *src, size_t count ); + +// Math functions +double ceil( double x ); +double floor( double x ); +double sqrt( double x ); +double sin( double x ); +double cos( double x ); +double atan2( double y, double x ); +double tan( double x ); +int abs( int n ); +double fabs( double x ); +double acos( double x ); + +#endif // BG_LIB_H diff --git a/reaction/engine/code/game/bg_local.h b/reaction/engine/code/game/bg_local.h new file mode 100644 index 00000000..246299ce --- /dev/null +++ b/reaction/engine/code/game/bg_local.h @@ -0,0 +1,83 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// bg_local.h -- local definitions for the bg (both games) files + +#define MIN_WALK_NORMAL 0.7f // can't walk on very steep slopes + +#define STEPSIZE 18 + +#define JUMP_VELOCITY 270 + +#define TIMER_LAND 130 +#define TIMER_GESTURE (34*66+50) + +#define OVERCLIP 1.001f + +// all of the locals will be zeroed before each +// pmove, just to make damn sure we don't have +// any differences when running on client or server +typedef struct { + vec3_t forward, right, up; + float frametime; + + int msec; + + qboolean walking; + qboolean groundPlane; + trace_t groundTrace; + + float impactSpeed; + + vec3_t previous_origin; + vec3_t previous_velocity; + int previous_waterlevel; +} pml_t; + +extern pmove_t *pm; +extern pml_t pml; + +// movement parameters +extern float pm_stopspeed; +extern float pm_duckScale; +extern float pm_swimScale; +extern float pm_wadeScale; + +extern float pm_accelerate; +extern float pm_airaccelerate; +extern float pm_wateraccelerate; +extern float pm_flyaccelerate; + +extern float pm_friction; +extern float pm_waterfriction; +extern float pm_flightfriction; + +extern int c_pmove; + +void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ); +void PM_AddTouchEnt( int entityNum ); +void PM_AddEvent( int newEvent ); + +qboolean PM_SlideMove( qboolean gravity ); +void PM_StepSlideMove( qboolean gravity ); + + diff --git a/reaction/engine/code/game/bg_misc.c b/reaction/engine/code/game/bg_misc.c new file mode 100644 index 00000000..a6ec86e2 --- /dev/null +++ b/reaction/engine/code/game/bg_misc.c @@ -0,0 +1,1604 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// bg_misc.c -- both games misc functions, all completely stateless + +#include "../qcommon/q_shared.h" +#include "bg_public.h" + +/*QUAKED item_***** ( 0 0 0 ) (-16 -16 -16) (16 16 16) suspended +DO NOT USE THIS CLASS, IT JUST HOLDS GENERAL INFORMATION. +The suspended flag will allow items to hang in the air, otherwise they are dropped to the next surface. + +If an item is the target of another entity, it will not spawn in until fired. + +An item fires all of its targets when it is picked up. If the toucher can't carry it, the targets won't be fired. + +"notfree" if set to 1, don't spawn in free for all games +"notteam" if set to 1, don't spawn in team games +"notsingle" if set to 1, don't spawn in single player games +"wait" override the default wait before respawning. -1 = never respawn automatically, which can be used with targeted spawning. +"random" random number of plus or minus seconds varied from the respawn time +"count" override quantity or duration on most items. +*/ + +gitem_t bg_itemlist[] = +{ + { + NULL, + NULL, + { NULL, + NULL, + NULL, NULL} , +/* icon */ NULL, +/* pickup */ NULL, + 0, + 0, + 0, +/* precache */ "", +/* sounds */ "" + }, // leave index 0 alone + + // + // ARMOR + // + +/*QUAKED item_armor_shard (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_armor_shard", + "sound/misc/ar1_pkup.wav", + { "models/powerups/armor/shard.md3", + "models/powerups/armor/shard_sphere.md3", + NULL, NULL} , +/* icon */ "icons/iconr_shard", +/* pickup */ "Armor Shard", + 5, + IT_ARMOR, + 0, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_armor_combat (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_armor_combat", + "sound/misc/ar2_pkup.wav", + { "models/powerups/armor/armor_yel.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconr_yellow", +/* pickup */ "Armor", + 50, + IT_ARMOR, + 0, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_armor_body (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_armor_body", + "sound/misc/ar2_pkup.wav", + { "models/powerups/armor/armor_red.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconr_red", +/* pickup */ "Heavy Armor", + 100, + IT_ARMOR, + 0, +/* precache */ "", +/* sounds */ "" + }, + + // + // health + // +/*QUAKED item_health_small (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_health_small", + "sound/items/s_health.wav", + { "models/powerups/health/small_cross.md3", + "models/powerups/health/small_sphere.md3", + NULL, NULL }, +/* icon */ "icons/iconh_green", +/* pickup */ "5 Health", + 5, + IT_HEALTH, + 0, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_health (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_health", + "sound/items/n_health.wav", + { "models/powerups/health/medium_cross.md3", + "models/powerups/health/medium_sphere.md3", + NULL, NULL }, +/* icon */ "icons/iconh_yellow", +/* pickup */ "25 Health", + 25, + IT_HEALTH, + 0, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_health_large (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_health_large", + "sound/items/l_health.wav", + { "models/powerups/health/large_cross.md3", + "models/powerups/health/large_sphere.md3", + NULL, NULL }, +/* icon */ "icons/iconh_red", +/* pickup */ "50 Health", + 50, + IT_HEALTH, + 0, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_health_mega (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_health_mega", + "sound/items/m_health.wav", + { "models/powerups/health/mega_cross.md3", + "models/powerups/health/mega_sphere.md3", + NULL, NULL }, +/* icon */ "icons/iconh_mega", +/* pickup */ "Mega Health", + 100, + IT_HEALTH, + 0, +/* precache */ "", +/* sounds */ "" + }, + + + // + // WEAPONS + // + +/*QUAKED weapon_gauntlet (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_gauntlet", + "sound/misc/w_pkup.wav", + { "models/weapons2/gauntlet/gauntlet.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_gauntlet", +/* pickup */ "Gauntlet", + 0, + IT_WEAPON, + WP_GAUNTLET, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED weapon_shotgun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_shotgun", + "sound/misc/w_pkup.wav", + { "models/weapons2/shotgun/shotgun.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_shotgun", +/* pickup */ "Shotgun", + 10, + IT_WEAPON, + WP_SHOTGUN, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED weapon_machinegun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_machinegun", + "sound/misc/w_pkup.wav", + { "models/weapons2/machinegun/machinegun.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_machinegun", +/* pickup */ "Machinegun", + 40, + IT_WEAPON, + WP_MACHINEGUN, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED weapon_grenadelauncher (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_grenadelauncher", + "sound/misc/w_pkup.wav", + { "models/weapons2/grenadel/grenadel.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_grenade", +/* pickup */ "Grenade Launcher", + 10, + IT_WEAPON, + WP_GRENADE_LAUNCHER, +/* precache */ "", +/* sounds */ "sound/weapons/grenade/hgrenb1a.wav sound/weapons/grenade/hgrenb2a.wav" + }, + +/*QUAKED weapon_rocketlauncher (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_rocketlauncher", + "sound/misc/w_pkup.wav", + { "models/weapons2/rocketl/rocketl.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_rocket", +/* pickup */ "Rocket Launcher", + 10, + IT_WEAPON, + WP_ROCKET_LAUNCHER, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED weapon_lightning (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_lightning", + "sound/misc/w_pkup.wav", + { "models/weapons2/lightning/lightning.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_lightning", +/* pickup */ "Lightning Gun", + 100, + IT_WEAPON, + WP_LIGHTNING, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED weapon_railgun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_railgun", + "sound/misc/w_pkup.wav", + { "models/weapons2/railgun/railgun.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_railgun", +/* pickup */ "Railgun", + 10, + IT_WEAPON, + WP_RAILGUN, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED weapon_plasmagun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_plasmagun", + "sound/misc/w_pkup.wav", + { "models/weapons2/plasma/plasma.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_plasma", +/* pickup */ "Plasma Gun", + 50, + IT_WEAPON, + WP_PLASMAGUN, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED weapon_bfg (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_bfg", + "sound/misc/w_pkup.wav", + { "models/weapons2/bfg/bfg.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_bfg", +/* pickup */ "BFG10K", + 20, + IT_WEAPON, + WP_BFG, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED weapon_grapplinghook (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_grapplinghook", + "sound/misc/w_pkup.wav", + { "models/weapons2/grapple/grapple.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_grapple", +/* pickup */ "Grappling Hook", + 0, + IT_WEAPON, + WP_GRAPPLING_HOOK, +/* precache */ "", +/* sounds */ "" + }, + + // + // AMMO ITEMS + // + +/*QUAKED ammo_shells (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_shells", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/shotgunam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_shotgun", +/* pickup */ "Shells", + 10, + IT_AMMO, + WP_SHOTGUN, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED ammo_bullets (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_bullets", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/machinegunam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_machinegun", +/* pickup */ "Bullets", + 50, + IT_AMMO, + WP_MACHINEGUN, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED ammo_grenades (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_grenades", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/grenadeam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_grenade", +/* pickup */ "Grenades", + 5, + IT_AMMO, + WP_GRENADE_LAUNCHER, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED ammo_cells (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_cells", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/plasmaam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_plasma", +/* pickup */ "Cells", + 30, + IT_AMMO, + WP_PLASMAGUN, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED ammo_lightning (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_lightning", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/lightningam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_lightning", +/* pickup */ "Lightning", + 60, + IT_AMMO, + WP_LIGHTNING, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED ammo_rockets (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_rockets", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/rocketam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_rocket", +/* pickup */ "Rockets", + 5, + IT_AMMO, + WP_ROCKET_LAUNCHER, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED ammo_slugs (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_slugs", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/railgunam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_railgun", +/* pickup */ "Slugs", + 10, + IT_AMMO, + WP_RAILGUN, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED ammo_bfg (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_bfg", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/bfgam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_bfg", +/* pickup */ "Bfg Ammo", + 15, + IT_AMMO, + WP_BFG, +/* precache */ "", +/* sounds */ "" + }, + + // + // HOLDABLE ITEMS + // +/*QUAKED holdable_teleporter (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "holdable_teleporter", + "sound/items/holdable.wav", + { "models/powerups/holdable/teleporter.md3", + NULL, NULL, NULL}, +/* icon */ "icons/teleporter", +/* pickup */ "Personal Teleporter", + 60, + IT_HOLDABLE, + HI_TELEPORTER, +/* precache */ "", +/* sounds */ "" + }, +/*QUAKED holdable_medkit (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "holdable_medkit", + "sound/items/holdable.wav", + { + "models/powerups/holdable/medkit.md3", + "models/powerups/holdable/medkit_sphere.md3", + NULL, NULL}, +/* icon */ "icons/medkit", +/* pickup */ "Medkit", + 60, + IT_HOLDABLE, + HI_MEDKIT, +/* precache */ "", +/* sounds */ "sound/items/use_medkit.wav" + }, + + // + // POWERUP ITEMS + // +/*QUAKED item_quad (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_quad", + "sound/items/quaddamage.wav", + { "models/powerups/instant/quad.md3", + "models/powerups/instant/quad_ring.md3", + NULL, NULL }, +/* icon */ "icons/quad", +/* pickup */ "Quad Damage", + 30, + IT_POWERUP, + PW_QUAD, +/* precache */ "", +/* sounds */ "sound/items/damage2.wav sound/items/damage3.wav" + }, + +/*QUAKED item_enviro (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_enviro", + "sound/items/protect.wav", + { "models/powerups/instant/enviro.md3", + "models/powerups/instant/enviro_ring.md3", + NULL, NULL }, +/* icon */ "icons/envirosuit", +/* pickup */ "Battle Suit", + 30, + IT_POWERUP, + PW_BATTLESUIT, +/* precache */ "", +/* sounds */ "sound/items/airout.wav sound/items/protect3.wav" + }, + +/*QUAKED item_haste (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_haste", + "sound/items/haste.wav", + { "models/powerups/instant/haste.md3", + "models/powerups/instant/haste_ring.md3", + NULL, NULL }, +/* icon */ "icons/haste", +/* pickup */ "Speed", + 30, + IT_POWERUP, + PW_HASTE, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_invis (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_invis", + "sound/items/invisibility.wav", + { "models/powerups/instant/invis.md3", + "models/powerups/instant/invis_ring.md3", + NULL, NULL }, +/* icon */ "icons/invis", +/* pickup */ "Invisibility", + 30, + IT_POWERUP, + PW_INVIS, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_regen (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_regen", + "sound/items/regeneration.wav", + { "models/powerups/instant/regen.md3", + "models/powerups/instant/regen_ring.md3", + NULL, NULL }, +/* icon */ "icons/regen", +/* pickup */ "Regeneration", + 30, + IT_POWERUP, + PW_REGEN, +/* precache */ "", +/* sounds */ "sound/items/regen.wav" + }, + +/*QUAKED item_flight (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_flight", + "sound/items/flight.wav", + { "models/powerups/instant/flight.md3", + "models/powerups/instant/flight_ring.md3", + NULL, NULL }, +/* icon */ "icons/flight", +/* pickup */ "Flight", + 60, + IT_POWERUP, + PW_FLIGHT, +/* precache */ "", +/* sounds */ "sound/items/flight.wav" + }, + +/*QUAKED team_CTF_redflag (1 0 0) (-16 -16 -16) (16 16 16) +Only in CTF games +*/ + { + "team_CTF_redflag", + NULL, + { "models/flags/r_flag.md3", + NULL, NULL, NULL }, +/* icon */ "icons/iconf_red1", +/* pickup */ "Red Flag", + 0, + IT_TEAM, + PW_REDFLAG, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED team_CTF_blueflag (0 0 1) (-16 -16 -16) (16 16 16) +Only in CTF games +*/ + { + "team_CTF_blueflag", + NULL, + { "models/flags/b_flag.md3", + NULL, NULL, NULL }, +/* icon */ "icons/iconf_blu1", +/* pickup */ "Blue Flag", + 0, + IT_TEAM, + PW_BLUEFLAG, +/* precache */ "", +/* sounds */ "" + }, + +#ifdef MISSIONPACK +/*QUAKED holdable_kamikaze (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "holdable_kamikaze", + "sound/items/holdable.wav", + { "models/powerups/kamikazi.md3", + NULL, NULL, NULL}, +/* icon */ "icons/kamikaze", +/* pickup */ "Kamikaze", + 60, + IT_HOLDABLE, + HI_KAMIKAZE, +/* precache */ "", +/* sounds */ "sound/items/kamikazerespawn.wav" + }, + +/*QUAKED holdable_portal (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "holdable_portal", + "sound/items/holdable.wav", + { "models/powerups/holdable/porter.md3", + NULL, NULL, NULL}, +/* icon */ "icons/portal", +/* pickup */ "Portal", + 60, + IT_HOLDABLE, + HI_PORTAL, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED holdable_invulnerability (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "holdable_invulnerability", + "sound/items/holdable.wav", + { "models/powerups/holdable/invulnerability.md3", + NULL, NULL, NULL}, +/* icon */ "icons/invulnerability", +/* pickup */ "Invulnerability", + 60, + IT_HOLDABLE, + HI_INVULNERABILITY, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED ammo_nails (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_nails", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/nailgunam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_nailgun", +/* pickup */ "Nails", + 20, + IT_AMMO, + WP_NAILGUN, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED ammo_mines (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_mines", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/proxmineam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_proxlauncher", +/* pickup */ "Proximity Mines", + 10, + IT_AMMO, + WP_PROX_LAUNCHER, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED ammo_belt (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_belt", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/chaingunam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_chaingun", +/* pickup */ "Chaingun Belt", + 100, + IT_AMMO, + WP_CHAINGUN, +/* precache */ "", +/* sounds */ "" + }, + + // + // PERSISTANT POWERUP ITEMS + // +/*QUAKED item_scout (.3 .3 1) (-16 -16 -16) (16 16 16) suspended redTeam blueTeam +*/ + { + "item_scout", + "sound/items/scout.wav", + { "models/powerups/scout.md3", + NULL, NULL, NULL }, +/* icon */ "icons/scout", +/* pickup */ "Scout", + 30, + IT_PERSISTANT_POWERUP, + PW_SCOUT, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_guard (.3 .3 1) (-16 -16 -16) (16 16 16) suspended redTeam blueTeam +*/ + { + "item_guard", + "sound/items/guard.wav", + { "models/powerups/guard.md3", + NULL, NULL, NULL }, +/* icon */ "icons/guard", +/* pickup */ "Guard", + 30, + IT_PERSISTANT_POWERUP, + PW_GUARD, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_doubler (.3 .3 1) (-16 -16 -16) (16 16 16) suspended redTeam blueTeam +*/ + { + "item_doubler", + "sound/items/doubler.wav", + { "models/powerups/doubler.md3", + NULL, NULL, NULL }, +/* icon */ "icons/doubler", +/* pickup */ "Doubler", + 30, + IT_PERSISTANT_POWERUP, + PW_DOUBLER, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_doubler (.3 .3 1) (-16 -16 -16) (16 16 16) suspended redTeam blueTeam +*/ + { + "item_ammoregen", + "sound/items/ammoregen.wav", + { "models/powerups/ammo.md3", + NULL, NULL, NULL }, +/* icon */ "icons/ammo_regen", +/* pickup */ "Ammo Regen", + 30, + IT_PERSISTANT_POWERUP, + PW_AMMOREGEN, +/* precache */ "", +/* sounds */ "" + }, + + /*QUAKED team_CTF_neutralflag (0 0 1) (-16 -16 -16) (16 16 16) +Only in One Flag CTF games +*/ + { + "team_CTF_neutralflag", + NULL, + { "models/flags/n_flag.md3", + NULL, NULL, NULL }, +/* icon */ "icons/iconf_neutral1", +/* pickup */ "Neutral Flag", + 0, + IT_TEAM, + PW_NEUTRALFLAG, +/* precache */ "", +/* sounds */ "" + }, + + { + "item_redcube", + "sound/misc/am_pkup.wav", + { "models/powerups/orb/r_orb.md3", + NULL, NULL, NULL }, +/* icon */ "icons/iconh_rorb", +/* pickup */ "Red Cube", + 0, + IT_TEAM, + 0, +/* precache */ "", +/* sounds */ "" + }, + + { + "item_bluecube", + "sound/misc/am_pkup.wav", + { "models/powerups/orb/b_orb.md3", + NULL, NULL, NULL }, +/* icon */ "icons/iconh_borb", +/* pickup */ "Blue Cube", + 0, + IT_TEAM, + 0, +/* precache */ "", +/* sounds */ "" + }, +/*QUAKED weapon_nailgun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_nailgun", + "sound/misc/w_pkup.wav", + { "models/weapons/nailgun/nailgun.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_nailgun", +/* pickup */ "Nailgun", + 10, + IT_WEAPON, + WP_NAILGUN, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED weapon_prox_launcher (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_prox_launcher", + "sound/misc/w_pkup.wav", + { "models/weapons/proxmine/proxmine.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_proxlauncher", +/* pickup */ "Prox Launcher", + 5, + IT_WEAPON, + WP_PROX_LAUNCHER, +/* precache */ "", +/* sounds */ "sound/weapons/proxmine/wstbtick.wav " + "sound/weapons/proxmine/wstbactv.wav " + "sound/weapons/proxmine/wstbimpl.wav " + "sound/weapons/proxmine/wstbimpm.wav " + "sound/weapons/proxmine/wstbimpd.wav " + "sound/weapons/proxmine/wstbactv.wav" + }, + +/*QUAKED weapon_chaingun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_chaingun", + "sound/misc/w_pkup.wav", + { "models/weapons/vulcan/vulcan.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_chaingun", +/* pickup */ "Chaingun", + 80, + IT_WEAPON, + WP_CHAINGUN, +/* precache */ "", +/* sounds */ "sound/weapons/vulcan/wvulwind.wav" + }, +#endif + + // end of list marker + {NULL} +}; + +int bg_numItems = sizeof(bg_itemlist) / sizeof(bg_itemlist[0]) - 1; + + +/* +============== +BG_FindItemForPowerup +============== +*/ +gitem_t *BG_FindItemForPowerup( powerup_t pw ) { + int i; + + for ( i = 0 ; i < bg_numItems ; i++ ) { + if ( (bg_itemlist[i].giType == IT_POWERUP || + bg_itemlist[i].giType == IT_TEAM || + bg_itemlist[i].giType == IT_PERSISTANT_POWERUP) && + bg_itemlist[i].giTag == pw ) { + return &bg_itemlist[i]; + } + } + + return NULL; +} + + +/* +============== +BG_FindItemForHoldable +============== +*/ +gitem_t *BG_FindItemForHoldable( holdable_t pw ) { + int i; + + for ( i = 0 ; i < bg_numItems ; i++ ) { + if ( bg_itemlist[i].giType == IT_HOLDABLE && bg_itemlist[i].giTag == pw ) { + return &bg_itemlist[i]; + } + } + + Com_Error( ERR_DROP, "HoldableItem not found" ); + + return NULL; +} + + +/* +=============== +BG_FindItemForWeapon + +=============== +*/ +gitem_t *BG_FindItemForWeapon( weapon_t weapon ) { + gitem_t *it; + + for ( it = bg_itemlist + 1 ; it->classname ; it++) { + if ( it->giType == IT_WEAPON && it->giTag == weapon ) { + return it; + } + } + + Com_Error( ERR_DROP, "Couldn't find item for weapon %i", weapon); + return NULL; +} + +/* +=============== +BG_FindItem + +=============== +*/ +gitem_t *BG_FindItem( const char *pickupName ) { + gitem_t *it; + + for ( it = bg_itemlist + 1 ; it->classname ; it++ ) { + if ( !Q_stricmp( it->pickup_name, pickupName ) ) + return it; + } + + return NULL; +} + +/* +============ +BG_PlayerTouchesItem + +Items can be picked up without actually touching their physical bounds to make +grabbing them easier +============ +*/ +qboolean BG_PlayerTouchesItem( playerState_t *ps, entityState_t *item, int atTime ) { + vec3_t origin; + + BG_EvaluateTrajectory( &item->pos, atTime, origin ); + + // we are ignoring ducked differences here + if ( ps->origin[0] - origin[0] > 44 + || ps->origin[0] - origin[0] < -50 + || ps->origin[1] - origin[1] > 36 + || ps->origin[1] - origin[1] < -36 + || ps->origin[2] - origin[2] > 36 + || ps->origin[2] - origin[2] < -36 ) { + return qfalse; + } + + return qtrue; +} + + + +/* +================ +BG_CanItemBeGrabbed + +Returns false if the item should not be picked up. +This needs to be the same for client side prediction and server use. +================ +*/ +qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const playerState_t *ps ) { + gitem_t *item; +#ifdef MISSIONPACK + int upperBound; +#endif + + if ( ent->modelindex < 1 || ent->modelindex >= bg_numItems ) { + Com_Error( ERR_DROP, "BG_CanItemBeGrabbed: index out of range" ); + } + + item = &bg_itemlist[ent->modelindex]; + + switch( item->giType ) { + case IT_WEAPON: + return qtrue; // weapons are always picked up + + case IT_AMMO: + if ( ps->ammo[ item->giTag ] >= 200 ) { + return qfalse; // can't hold any more + } + return qtrue; + + case IT_ARMOR: +#ifdef MISSIONPACK + if( bg_itemlist[ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) { + return qfalse; + } + + // we also clamp armor to the maxhealth for handicapping + if( bg_itemlist[ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { + upperBound = ps->stats[STAT_MAX_HEALTH]; + } + else { + upperBound = ps->stats[STAT_MAX_HEALTH] * 2; + } + + if ( ps->stats[STAT_ARMOR] >= upperBound ) { + return qfalse; + } +#else + if ( ps->stats[STAT_ARMOR] >= ps->stats[STAT_MAX_HEALTH] * 2 ) { + return qfalse; + } +#endif + return qtrue; + + case IT_HEALTH: + // small and mega healths will go over the max, otherwise + // don't pick up if already at max +#ifdef MISSIONPACK + if( bg_itemlist[ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { + upperBound = ps->stats[STAT_MAX_HEALTH]; + } + else +#endif + if ( item->quantity == 5 || item->quantity == 100 ) { + if ( ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH] * 2 ) { + return qfalse; + } + return qtrue; + } + + if ( ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH] ) { + return qfalse; + } + return qtrue; + + case IT_POWERUP: + return qtrue; // powerups are always picked up + +#ifdef MISSIONPACK + case IT_PERSISTANT_POWERUP: + // can only hold one item at a time + if ( ps->stats[STAT_PERSISTANT_POWERUP] ) { + return qfalse; + } + + // check team only + if( ( ent->generic1 & 2 ) && ( ps->persistant[PERS_TEAM] != TEAM_RED ) ) { + return qfalse; + } + if( ( ent->generic1 & 4 ) && ( ps->persistant[PERS_TEAM] != TEAM_BLUE ) ) { + return qfalse; + } + + return qtrue; +#endif + + case IT_TEAM: // team items, such as flags +#ifdef MISSIONPACK + if( gametype == GT_1FCTF ) { + // neutral flag can always be picked up + if( item->giTag == PW_NEUTRALFLAG ) { + return qtrue; + } + if (ps->persistant[PERS_TEAM] == TEAM_RED) { + if (item->giTag == PW_BLUEFLAG && ps->powerups[PW_NEUTRALFLAG] ) { + return qtrue; + } + } else if (ps->persistant[PERS_TEAM] == TEAM_BLUE) { + if (item->giTag == PW_REDFLAG && ps->powerups[PW_NEUTRALFLAG] ) { + return qtrue; + } + } + } +#endif + if( gametype == GT_CTF ) { + // ent->modelindex2 is non-zero on items if they are dropped + // we need to know this because we can pick up our dropped flag (and return it) + // but we can't pick up our flag at base + if (ps->persistant[PERS_TEAM] == TEAM_RED) { + if (item->giTag == PW_BLUEFLAG || + (item->giTag == PW_REDFLAG && ent->modelindex2) || + (item->giTag == PW_REDFLAG && ps->powerups[PW_BLUEFLAG]) ) + return qtrue; + } else if (ps->persistant[PERS_TEAM] == TEAM_BLUE) { + if (item->giTag == PW_REDFLAG || + (item->giTag == PW_BLUEFLAG && ent->modelindex2) || + (item->giTag == PW_BLUEFLAG && ps->powerups[PW_REDFLAG]) ) + return qtrue; + } + } + +#ifdef MISSIONPACK + if( gametype == GT_HARVESTER ) { + return qtrue; + } +#endif + return qfalse; + + case IT_HOLDABLE: + // can only hold one item at a time + if ( ps->stats[STAT_HOLDABLE_ITEM] ) { + return qfalse; + } + return qtrue; + + case IT_BAD: + Com_Error( ERR_DROP, "BG_CanItemBeGrabbed: IT_BAD" ); + default: +#ifndef Q3_VM +#ifndef NDEBUG + Com_Printf("BG_CanItemBeGrabbed: unknown enum %d\n", item->giType ); +#endif +#endif + break; + } + + return qfalse; +} + +//====================================================================== + +/* +================ +BG_EvaluateTrajectory + +================ +*/ +void BG_EvaluateTrajectory( const trajectory_t *tr, int atTime, vec3_t result ) { + float deltaTime; + float phase; + + switch( tr->trType ) { + case TR_STATIONARY: + case TR_INTERPOLATE: + VectorCopy( tr->trBase, result ); + break; + case TR_LINEAR: + deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds + VectorMA( tr->trBase, deltaTime, tr->trDelta, result ); + break; + case TR_SINE: + deltaTime = ( atTime - tr->trTime ) / (float) tr->trDuration; + phase = sin( deltaTime * M_PI * 2 ); + VectorMA( tr->trBase, phase, tr->trDelta, result ); + break; + case TR_LINEAR_STOP: + if ( atTime > tr->trTime + tr->trDuration ) { + atTime = tr->trTime + tr->trDuration; + } + deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds + if ( deltaTime < 0 ) { + deltaTime = 0; + } + VectorMA( tr->trBase, deltaTime, tr->trDelta, result ); + break; + case TR_GRAVITY: + deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds + VectorMA( tr->trBase, deltaTime, tr->trDelta, result ); + result[2] -= 0.5 * DEFAULT_GRAVITY * deltaTime * deltaTime; // FIXME: local gravity... + break; + default: + Com_Error( ERR_DROP, "BG_EvaluateTrajectory: unknown trType: %i", tr->trTime ); + break; + } +} + +/* +================ +BG_EvaluateTrajectoryDelta + +For determining velocity at a given time +================ +*/ +void BG_EvaluateTrajectoryDelta( const trajectory_t *tr, int atTime, vec3_t result ) { + float deltaTime; + float phase; + + switch( tr->trType ) { + case TR_STATIONARY: + case TR_INTERPOLATE: + VectorClear( result ); + break; + case TR_LINEAR: + VectorCopy( tr->trDelta, result ); + break; + case TR_SINE: + deltaTime = ( atTime - tr->trTime ) / (float) tr->trDuration; + phase = cos( deltaTime * M_PI * 2 ); // derivative of sin = cos + phase *= 0.5; + VectorScale( tr->trDelta, phase, result ); + break; + case TR_LINEAR_STOP: + if ( atTime > tr->trTime + tr->trDuration ) { + VectorClear( result ); + return; + } + VectorCopy( tr->trDelta, result ); + break; + case TR_GRAVITY: + deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds + VectorCopy( tr->trDelta, result ); + result[2] -= DEFAULT_GRAVITY * deltaTime; // FIXME: local gravity... + break; + default: + Com_Error( ERR_DROP, "BG_EvaluateTrajectoryDelta: unknown trType: %i", tr->trTime ); + break; + } +} + +char *eventnames[] = { + "EV_NONE", + + "EV_FOOTSTEP", + "EV_FOOTSTEP_METAL", + "EV_FOOTSPLASH", + "EV_FOOTWADE", + "EV_SWIM", + + "EV_STEP_4", + "EV_STEP_8", + "EV_STEP_12", + "EV_STEP_16", + + "EV_FALL_SHORT", + "EV_FALL_MEDIUM", + "EV_FALL_FAR", + + "EV_JUMP_PAD", // boing sound at origin", jump sound on player + + "EV_JUMP", + "EV_WATER_TOUCH", // foot touches + "EV_WATER_LEAVE", // foot leaves + "EV_WATER_UNDER", // head touches + "EV_WATER_CLEAR", // head leaves + + "EV_ITEM_PICKUP", // normal item pickups are predictable + "EV_GLOBAL_ITEM_PICKUP", // powerup / team sounds are broadcast to everyone + + "EV_NOAMMO", + "EV_CHANGE_WEAPON", + "EV_FIRE_WEAPON", + + "EV_USE_ITEM0", + "EV_USE_ITEM1", + "EV_USE_ITEM2", + "EV_USE_ITEM3", + "EV_USE_ITEM4", + "EV_USE_ITEM5", + "EV_USE_ITEM6", + "EV_USE_ITEM7", + "EV_USE_ITEM8", + "EV_USE_ITEM9", + "EV_USE_ITEM10", + "EV_USE_ITEM11", + "EV_USE_ITEM12", + "EV_USE_ITEM13", + "EV_USE_ITEM14", + "EV_USE_ITEM15", + + "EV_ITEM_RESPAWN", + "EV_ITEM_POP", + "EV_PLAYER_TELEPORT_IN", + "EV_PLAYER_TELEPORT_OUT", + + "EV_GRENADE_BOUNCE", // eventParm will be the soundindex + + "EV_GENERAL_SOUND", + "EV_GLOBAL_SOUND", // no attenuation + "EV_GLOBAL_TEAM_SOUND", + + "EV_BULLET_HIT_FLESH", + "EV_BULLET_HIT_WALL", + + "EV_MISSILE_HIT", + "EV_MISSILE_MISS", + "EV_MISSILE_MISS_METAL", + "EV_RAILTRAIL", + "EV_SHOTGUN", + "EV_BULLET", // otherEntity is the shooter + + "EV_PAIN", + "EV_DEATH1", + "EV_DEATH2", + "EV_DEATH3", + "EV_OBITUARY", + + "EV_POWERUP_QUAD", + "EV_POWERUP_BATTLESUIT", + "EV_POWERUP_REGEN", + + "EV_GIB_PLAYER", // gib a previously living player + "EV_SCOREPLUM", // score plum + +//#ifdef MISSIONPACK + "EV_PROXIMITY_MINE_STICK", + "EV_PROXIMITY_MINE_TRIGGER", + "EV_KAMIKAZE", // kamikaze explodes + "EV_OBELISKEXPLODE", // obelisk explodes + "EV_INVUL_IMPACT", // invulnerability sphere impact + "EV_JUICED", // invulnerability juiced effect + "EV_LIGHTNINGBOLT", // lightning bolt bounced of invulnerability sphere +//#endif + + "EV_DEBUG_LINE", + "EV_STOPLOOPINGSOUND", + "EV_TAUNT" + +}; + +/* +=============== +BG_AddPredictableEventToPlayerstate + +Handles the sequence numbers +=============== +*/ + +void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); + +void BG_AddPredictableEventToPlayerstate( int newEvent, int eventParm, playerState_t *ps ) { + +#ifdef _DEBUG + { + char buf[256]; + trap_Cvar_VariableStringBuffer("showevents", buf, sizeof(buf)); + if ( atof(buf) != 0 ) { +#ifdef QAGAME + Com_Printf(" game event svt %5d -> %5d: num = %20s parm %d\n", ps->pmove_framecount/*ps->commandTime*/, ps->eventSequence, eventnames[newEvent], eventParm); +#else + Com_Printf("Cgame event svt %5d -> %5d: num = %20s parm %d\n", ps->pmove_framecount/*ps->commandTime*/, ps->eventSequence, eventnames[newEvent], eventParm); +#endif + } + } +#endif + ps->events[ps->eventSequence & (MAX_PS_EVENTS-1)] = newEvent; + ps->eventParms[ps->eventSequence & (MAX_PS_EVENTS-1)] = eventParm; + ps->eventSequence++; +} + +/* +======================== +BG_TouchJumpPad +======================== +*/ +void BG_TouchJumpPad( playerState_t *ps, entityState_t *jumppad ) { + vec3_t angles; + float p; + int effectNum; + + // spectators don't use jump pads + if ( ps->pm_type != PM_NORMAL ) { + return; + } + + // flying characters don't hit bounce pads + if ( ps->powerups[PW_FLIGHT] ) { + return; + } + + // if we didn't hit this same jumppad the previous frame + // then don't play the event sound again if we are in a fat trigger + if ( ps->jumppad_ent != jumppad->number ) { + + vectoangles( jumppad->origin2, angles); + p = fabs( AngleNormalize180( angles[PITCH] ) ); + if( p < 45 ) { + effectNum = 0; + } else { + effectNum = 1; + } + BG_AddPredictableEventToPlayerstate( EV_JUMP_PAD, effectNum, ps ); + } + // remember hitting this jumppad this frame + ps->jumppad_ent = jumppad->number; + ps->jumppad_frame = ps->pmove_framecount; + // give the player the velocity from the jumppad + VectorCopy( jumppad->origin2, ps->velocity ); +} + +/* +======================== +BG_PlayerStateToEntityState + +This is done after each set of usercmd_t on the server, +and after local prediction on the client +======================== +*/ +void BG_PlayerStateToEntityState( playerState_t *ps, entityState_t *s, qboolean snap ) { + int i; + + if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPECTATOR ) { + s->eType = ET_INVISIBLE; + } else if ( ps->stats[STAT_HEALTH] <= GIB_HEALTH ) { + s->eType = ET_INVISIBLE; + } else { + s->eType = ET_PLAYER; + } + + s->number = ps->clientNum; + + s->pos.trType = TR_INTERPOLATE; + VectorCopy( ps->origin, s->pos.trBase ); + if ( snap ) { + SnapVector( s->pos.trBase ); + } + // set the trDelta for flag direction + VectorCopy( ps->velocity, s->pos.trDelta ); + + s->apos.trType = TR_INTERPOLATE; + VectorCopy( ps->viewangles, s->apos.trBase ); + if ( snap ) { + SnapVector( s->apos.trBase ); + } + + s->angles2[YAW] = ps->movementDir; + s->legsAnim = ps->legsAnim; + s->torsoAnim = ps->torsoAnim; + s->clientNum = ps->clientNum; // ET_PLAYER looks here instead of at number + // so corpses can also reference the proper config + s->eFlags = ps->eFlags; + if ( ps->stats[STAT_HEALTH] <= 0 ) { + s->eFlags |= EF_DEAD; + } else { + s->eFlags &= ~EF_DEAD; + } + + if ( ps->externalEvent ) { + s->event = ps->externalEvent; + s->eventParm = ps->externalEventParm; + } else if ( ps->entityEventSequence < ps->eventSequence ) { + int seq; + + if ( ps->entityEventSequence < ps->eventSequence - MAX_PS_EVENTS) { + ps->entityEventSequence = ps->eventSequence - MAX_PS_EVENTS; + } + seq = ps->entityEventSequence & (MAX_PS_EVENTS-1); + s->event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 ); + s->eventParm = ps->eventParms[ seq ]; + ps->entityEventSequence++; + } + + s->weapon = ps->weapon; + s->groundEntityNum = ps->groundEntityNum; + + s->powerups = 0; + for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { + if ( ps->powerups[ i ] ) { + s->powerups |= 1 << i; + } + } + + s->loopSound = ps->loopSound; + s->generic1 = ps->generic1; +} + +/* +======================== +BG_PlayerStateToEntityStateExtraPolate + +This is done after each set of usercmd_t on the server, +and after local prediction on the client +======================== +*/ +void BG_PlayerStateToEntityStateExtraPolate( playerState_t *ps, entityState_t *s, int time, qboolean snap ) { + int i; + + if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPECTATOR ) { + s->eType = ET_INVISIBLE; + } else if ( ps->stats[STAT_HEALTH] <= GIB_HEALTH ) { + s->eType = ET_INVISIBLE; + } else { + s->eType = ET_PLAYER; + } + + s->number = ps->clientNum; + + s->pos.trType = TR_LINEAR_STOP; + VectorCopy( ps->origin, s->pos.trBase ); + if ( snap ) { + SnapVector( s->pos.trBase ); + } + // set the trDelta for flag direction and linear prediction + VectorCopy( ps->velocity, s->pos.trDelta ); + // set the time for linear prediction + s->pos.trTime = time; + // set maximum extra polation time + s->pos.trDuration = 50; // 1000 / sv_fps (default = 20) + + s->apos.trType = TR_INTERPOLATE; + VectorCopy( ps->viewangles, s->apos.trBase ); + if ( snap ) { + SnapVector( s->apos.trBase ); + } + + s->angles2[YAW] = ps->movementDir; + s->legsAnim = ps->legsAnim; + s->torsoAnim = ps->torsoAnim; + s->clientNum = ps->clientNum; // ET_PLAYER looks here instead of at number + // so corpses can also reference the proper config + s->eFlags = ps->eFlags; + if ( ps->stats[STAT_HEALTH] <= 0 ) { + s->eFlags |= EF_DEAD; + } else { + s->eFlags &= ~EF_DEAD; + } + + if ( ps->externalEvent ) { + s->event = ps->externalEvent; + s->eventParm = ps->externalEventParm; + } else if ( ps->entityEventSequence < ps->eventSequence ) { + int seq; + + if ( ps->entityEventSequence < ps->eventSequence - MAX_PS_EVENTS) { + ps->entityEventSequence = ps->eventSequence - MAX_PS_EVENTS; + } + seq = ps->entityEventSequence & (MAX_PS_EVENTS-1); + s->event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 ); + s->eventParm = ps->eventParms[ seq ]; + ps->entityEventSequence++; + } + + s->weapon = ps->weapon; + s->groundEntityNum = ps->groundEntityNum; + + s->powerups = 0; + for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { + if ( ps->powerups[ i ] ) { + s->powerups |= 1 << i; + } + } + + s->loopSound = ps->loopSound; + s->generic1 = ps->generic1; +} diff --git a/reaction/engine/code/game/bg_pmove.c b/reaction/engine/code/game/bg_pmove.c new file mode 100644 index 00000000..f31222a4 --- /dev/null +++ b/reaction/engine/code/game/bg_pmove.c @@ -0,0 +1,2069 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// bg_pmove.c -- both games player movement code +// takes a playerstate and a usercmd as input and returns a modifed playerstate + +#include "../qcommon/q_shared.h" +#include "bg_public.h" +#include "bg_local.h" + +pmove_t *pm; +pml_t pml; + +// movement parameters +float pm_stopspeed = 100.0f; +float pm_duckScale = 0.25f; +float pm_swimScale = 0.50f; +float pm_wadeScale = 0.70f; + +float pm_accelerate = 10.0f; +float pm_airaccelerate = 1.0f; +float pm_wateraccelerate = 4.0f; +float pm_flyaccelerate = 8.0f; + +float pm_friction = 6.0f; +float pm_waterfriction = 1.0f; +float pm_flightfriction = 3.0f; +float pm_spectatorfriction = 5.0f; + +int c_pmove = 0; + + +/* +=============== +PM_AddEvent + +=============== +*/ +void PM_AddEvent( int newEvent ) { + BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps ); +} + +/* +=============== +PM_AddTouchEnt +=============== +*/ +void PM_AddTouchEnt( int entityNum ) { + int i; + + if ( entityNum == ENTITYNUM_WORLD ) { + return; + } + if ( pm->numtouch == MAXTOUCH ) { + return; + } + + // see if it is already added + for ( i = 0 ; i < pm->numtouch ; i++ ) { + if ( pm->touchents[ i ] == entityNum ) { + return; + } + } + + // add it + pm->touchents[pm->numtouch] = entityNum; + pm->numtouch++; +} + +/* +=================== +PM_StartTorsoAnim +=================== +*/ +static void PM_StartTorsoAnim( int anim ) { + if ( pm->ps->pm_type >= PM_DEAD ) { + return; + } + pm->ps->torsoAnim = ( ( pm->ps->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) + | anim; +} +static void PM_StartLegsAnim( int anim ) { + if ( pm->ps->pm_type >= PM_DEAD ) { + return; + } + if ( pm->ps->legsTimer > 0 ) { + return; // a high priority animation is running + } + pm->ps->legsAnim = ( ( pm->ps->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) + | anim; +} + +static void PM_ContinueLegsAnim( int anim ) { + if ( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == anim ) { + return; + } + if ( pm->ps->legsTimer > 0 ) { + return; // a high priority animation is running + } + PM_StartLegsAnim( anim ); +} + +static void PM_ContinueTorsoAnim( int anim ) { + if ( ( pm->ps->torsoAnim & ~ANIM_TOGGLEBIT ) == anim ) { + return; + } + if ( pm->ps->torsoTimer > 0 ) { + return; // a high priority animation is running + } + PM_StartTorsoAnim( anim ); +} + +static void PM_ForceLegsAnim( int anim ) { + pm->ps->legsTimer = 0; + PM_StartLegsAnim( anim ); +} + + +/* +================== +PM_ClipVelocity + +Slide off of the impacting surface +================== +*/ +void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) { + float backoff; + float change; + int i; + + backoff = DotProduct (in, normal); + + if ( backoff < 0 ) { + backoff *= overbounce; + } else { + backoff /= overbounce; + } + + for ( i=0 ; i<3 ; i++ ) { + change = normal[i]*backoff; + out[i] = in[i] - change; + } +} + + +/* +================== +PM_Friction + +Handles both ground friction and water friction +================== +*/ +static void PM_Friction( void ) { + vec3_t vec; + float *vel; + float speed, newspeed, control; + float drop; + + vel = pm->ps->velocity; + + VectorCopy( vel, vec ); + if ( pml.walking ) { + vec[2] = 0; // ignore slope movement + } + + speed = VectorLength(vec); + if (speed < 1) { + vel[0] = 0; + vel[1] = 0; // allow sinking underwater + // FIXME: still have z friction underwater? + return; + } + + drop = 0; + + // apply ground friction + if ( pm->waterlevel <= 1 ) { + if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) { + // if getting knocked back, no friction + if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) { + control = speed < pm_stopspeed ? pm_stopspeed : speed; + drop += control*pm_friction*pml.frametime; + } + } + } + + // apply water friction even if just wading + if ( pm->waterlevel ) { + drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime; + } + + // apply flying friction + if ( pm->ps->powerups[PW_FLIGHT]) { + drop += speed*pm_flightfriction*pml.frametime; + } + + if ( pm->ps->pm_type == PM_SPECTATOR) { + drop += speed*pm_spectatorfriction*pml.frametime; + } + + // scale the velocity + newspeed = speed - drop; + if (newspeed < 0) { + newspeed = 0; + } + newspeed /= speed; + + vel[0] = vel[0] * newspeed; + vel[1] = vel[1] * newspeed; + vel[2] = vel[2] * newspeed; +} + + +/* +============== +PM_Accelerate + +Handles user intended acceleration +============== +*/ +static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel ) { +#if 1 + // q2 style + int i; + float addspeed, accelspeed, currentspeed; + + currentspeed = DotProduct (pm->ps->velocity, wishdir); + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) { + return; + } + accelspeed = accel*pml.frametime*wishspeed; + if (accelspeed > addspeed) { + accelspeed = addspeed; + } + + for (i=0 ; i<3 ; i++) { + pm->ps->velocity[i] += accelspeed*wishdir[i]; + } +#else + // proper way (avoids strafe jump maxspeed bug), but feels bad + vec3_t wishVelocity; + vec3_t pushDir; + float pushLen; + float canPush; + + VectorScale( wishdir, wishspeed, wishVelocity ); + VectorSubtract( wishVelocity, pm->ps->velocity, pushDir ); + pushLen = VectorNormalize( pushDir ); + + canPush = accel*pml.frametime*wishspeed; + if (canPush > pushLen) { + canPush = pushLen; + } + + VectorMA( pm->ps->velocity, canPush, pushDir, pm->ps->velocity ); +#endif +} + + + +/* +============ +PM_CmdScale + +Returns the scale factor to apply to cmd movements +This allows the clients to use axial -127 to 127 values for all directions +without getting a sqrt(2) distortion in speed. +============ +*/ +static float PM_CmdScale( usercmd_t *cmd ) { + int max; + float total; + float scale; + + max = abs( cmd->forwardmove ); + if ( abs( cmd->rightmove ) > max ) { + max = abs( cmd->rightmove ); + } + if ( abs( cmd->upmove ) > max ) { + max = abs( cmd->upmove ); + } + if ( !max ) { + return 0; + } + + total = sqrt( cmd->forwardmove * cmd->forwardmove + + cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove ); + scale = (float)pm->ps->speed * max / ( 127.0 * total ); + + return scale; +} + + +/* +================ +PM_SetMovementDir + +Determine the rotation of the legs reletive +to the facing dir +================ +*/ +static void PM_SetMovementDir( void ) { + if ( pm->cmd.forwardmove || pm->cmd.rightmove ) { + if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) { + pm->ps->movementDir = 0; + } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) { + pm->ps->movementDir = 1; + } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) { + pm->ps->movementDir = 2; + } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) { + pm->ps->movementDir = 3; + } else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) { + pm->ps->movementDir = 4; + } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) { + pm->ps->movementDir = 5; + } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) { + pm->ps->movementDir = 6; + } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) { + pm->ps->movementDir = 7; + } + } else { + // if they aren't actively going directly sideways, + // change the animation to the diagonal so they + // don't stop too crooked + if ( pm->ps->movementDir == 2 ) { + pm->ps->movementDir = 1; + } else if ( pm->ps->movementDir == 6 ) { + pm->ps->movementDir = 7; + } + } +} + + +/* +============= +PM_CheckJump +============= +*/ +static qboolean PM_CheckJump( void ) { + if ( pm->ps->pm_flags & PMF_RESPAWNED ) { + return qfalse; // don't allow jump until all buttons are up + } + + if ( pm->cmd.upmove < 10 ) { + // not holding jump + return qfalse; + } + + // must wait for jump to be released + if ( pm->ps->pm_flags & PMF_JUMP_HELD ) { + // clear upmove so cmdscale doesn't lower running speed + pm->cmd.upmove = 0; + return qfalse; + } + + pml.groundPlane = qfalse; // jumping away + pml.walking = qfalse; + pm->ps->pm_flags |= PMF_JUMP_HELD; + + pm->ps->groundEntityNum = ENTITYNUM_NONE; + pm->ps->velocity[2] = JUMP_VELOCITY; + PM_AddEvent( EV_JUMP ); + + if ( pm->cmd.forwardmove >= 0 ) { + PM_ForceLegsAnim( LEGS_JUMP ); + pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; + } else { + PM_ForceLegsAnim( LEGS_JUMPB ); + pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; + } + + return qtrue; +} + +/* +============= +PM_CheckWaterJump +============= +*/ +static qboolean PM_CheckWaterJump( void ) { + vec3_t spot; + int cont; + vec3_t flatforward; + + if (pm->ps->pm_time) { + return qfalse; + } + + // check for water jump + if ( pm->waterlevel != 2 ) { + return qfalse; + } + + flatforward[0] = pml.forward[0]; + flatforward[1] = pml.forward[1]; + flatforward[2] = 0; + VectorNormalize (flatforward); + + VectorMA (pm->ps->origin, 30, flatforward, spot); + spot[2] += 4; + cont = pm->pointcontents (spot, pm->ps->clientNum ); + if ( !(cont & CONTENTS_SOLID) ) { + return qfalse; + } + + spot[2] += 16; + cont = pm->pointcontents (spot, pm->ps->clientNum ); + if ( cont ) { + return qfalse; + } + + // jump out of water + VectorScale (pml.forward, 200, pm->ps->velocity); + pm->ps->velocity[2] = 350; + + pm->ps->pm_flags |= PMF_TIME_WATERJUMP; + pm->ps->pm_time = 2000; + + return qtrue; +} + +//============================================================================ + + +/* +=================== +PM_WaterJumpMove + +Flying out of the water +=================== +*/ +static void PM_WaterJumpMove( void ) { + // waterjump has no control, but falls + + PM_StepSlideMove( qtrue ); + + pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; + if (pm->ps->velocity[2] < 0) { + // cancel as soon as we are falling down again + pm->ps->pm_flags &= ~PMF_ALL_TIMES; + pm->ps->pm_time = 0; + } +} + +/* +=================== +PM_WaterMove + +=================== +*/ +static void PM_WaterMove( void ) { + int i; + vec3_t wishvel; + float wishspeed; + vec3_t wishdir; + float scale; + float vel; + + if ( PM_CheckWaterJump() ) { + PM_WaterJumpMove(); + return; + } +#if 0 + // jump = head for surface + if ( pm->cmd.upmove >= 10 ) { + if (pm->ps->velocity[2] > -300) { + if ( pm->watertype == CONTENTS_WATER ) { + pm->ps->velocity[2] = 100; + } else if (pm->watertype == CONTENTS_SLIME) { + pm->ps->velocity[2] = 80; + } else { + pm->ps->velocity[2] = 50; + } + } + } +#endif + PM_Friction (); + + scale = PM_CmdScale( &pm->cmd ); + // + // user intentions + // + if ( !scale ) { + wishvel[0] = 0; + wishvel[1] = 0; + wishvel[2] = -60; // sink towards bottom + } else { + for (i=0 ; i<3 ; i++) + wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove; + + wishvel[2] += scale * pm->cmd.upmove; + } + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + if ( wishspeed > pm->ps->speed * pm_swimScale ) { + wishspeed = pm->ps->speed * pm_swimScale; + } + + PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate); + + // make sure we can go up slopes easily under water + if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) { + vel = VectorLength(pm->ps->velocity); + // slide along the ground plane + PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, + pm->ps->velocity, OVERCLIP ); + + VectorNormalize(pm->ps->velocity); + VectorScale(pm->ps->velocity, vel, pm->ps->velocity); + } + + PM_SlideMove( qfalse ); +} + +#ifdef MISSIONPACK +/* +=================== +PM_InvulnerabilityMove + +Only with the invulnerability powerup +=================== +*/ +static void PM_InvulnerabilityMove( void ) { + pm->cmd.forwardmove = 0; + pm->cmd.rightmove = 0; + pm->cmd.upmove = 0; + VectorClear(pm->ps->velocity); +} +#endif + +/* +=================== +PM_FlyMove + +Only with the flight powerup +=================== +*/ +static void PM_FlyMove( void ) { + int i; + vec3_t wishvel; + float wishspeed; + vec3_t wishdir; + float scale; + + // normal slowdown + PM_Friction (); + + scale = PM_CmdScale( &pm->cmd ); + // + // user intentions + // + if ( !scale ) { + wishvel[0] = 0; + wishvel[1] = 0; + wishvel[2] = 0; + } else { + for (i=0 ; i<3 ; i++) { + wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove; + } + + wishvel[2] += scale * pm->cmd.upmove; + } + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate); + + PM_StepSlideMove( qfalse ); +} + + +/* +=================== +PM_AirMove + +=================== +*/ +static void PM_AirMove( void ) { + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + float wishspeed; + float scale; + usercmd_t cmd; + + PM_Friction(); + + fmove = pm->cmd.forwardmove; + smove = pm->cmd.rightmove; + + cmd = pm->cmd; + scale = PM_CmdScale( &cmd ); + + // set the movementDir so clients can rotate the legs for strafing + PM_SetMovementDir(); + + // project moves down to flat plane + pml.forward[2] = 0; + pml.right[2] = 0; + VectorNormalize (pml.forward); + VectorNormalize (pml.right); + + for ( i = 0 ; i < 2 ; i++ ) { + wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; + } + wishvel[2] = 0; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + wishspeed *= scale; + + // not on ground, so little effect on velocity + PM_Accelerate (wishdir, wishspeed, pm_airaccelerate); + + // we may have a ground plane that is very steep, even + // though we don't have a groundentity + // slide along the steep plane + if ( pml.groundPlane ) { + PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, + pm->ps->velocity, OVERCLIP ); + } + +#if 0 + //ZOID: If we are on the grapple, try stair-stepping + //this allows a player to use the grapple to pull himself + //over a ledge + if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) + PM_StepSlideMove ( qtrue ); + else + PM_SlideMove ( qtrue ); +#endif + + PM_StepSlideMove ( qtrue ); +} + +/* +=================== +PM_GrappleMove + +=================== +*/ +static void PM_GrappleMove( void ) { + vec3_t vel, v; + float vlen; + + VectorScale(pml.forward, -16, v); + VectorAdd(pm->ps->grapplePoint, v, v); + VectorSubtract(v, pm->ps->origin, vel); + vlen = VectorLength(vel); + VectorNormalize( vel ); + + if (vlen <= 100) + VectorScale(vel, 10 * vlen, vel); + else + VectorScale(vel, 800, vel); + + VectorCopy(vel, pm->ps->velocity); + + pml.groundPlane = qfalse; +} + +/* +=================== +PM_WalkMove + +=================== +*/ +static void PM_WalkMove( void ) { + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + float wishspeed; + float scale; + usercmd_t cmd; + float accelerate; + float vel; + + if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) { + // begin swimming + PM_WaterMove(); + return; + } + + + if ( PM_CheckJump () ) { + // jumped away + if ( pm->waterlevel > 1 ) { + PM_WaterMove(); + } else { + PM_AirMove(); + } + return; + } + + PM_Friction (); + + fmove = pm->cmd.forwardmove; + smove = pm->cmd.rightmove; + + cmd = pm->cmd; + scale = PM_CmdScale( &cmd ); + + // set the movementDir so clients can rotate the legs for strafing + PM_SetMovementDir(); + + // project moves down to flat plane + pml.forward[2] = 0; + pml.right[2] = 0; + + // project the forward and right directions onto the ground plane + PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP ); + PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP ); + // + VectorNormalize (pml.forward); + VectorNormalize (pml.right); + + for ( i = 0 ; i < 3 ; i++ ) { + wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; + } + // when going up or down slopes the wish velocity should Not be zero +// wishvel[2] = 0; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + wishspeed *= scale; + + // clamp the speed lower if ducking + if ( pm->ps->pm_flags & PMF_DUCKED ) { + if ( wishspeed > pm->ps->speed * pm_duckScale ) { + wishspeed = pm->ps->speed * pm_duckScale; + } + } + + // clamp the speed lower if wading or walking on the bottom + if ( pm->waterlevel ) { + float waterScale; + + waterScale = pm->waterlevel / 3.0; + waterScale = 1.0 - ( 1.0 - pm_swimScale ) * waterScale; + if ( wishspeed > pm->ps->speed * waterScale ) { + wishspeed = pm->ps->speed * waterScale; + } + } + + // when a player gets hit, they temporarily lose + // full control, which allows them to be moved a bit + if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) { + accelerate = pm_airaccelerate; + } else { + accelerate = pm_accelerate; + } + + PM_Accelerate (wishdir, wishspeed, accelerate); + + //Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]); + //Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity)); + + if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) { + pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; + } else { + // don't reset the z velocity for slopes +// pm->ps->velocity[2] = 0; + } + + vel = VectorLength(pm->ps->velocity); + + // slide along the ground plane + PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, + pm->ps->velocity, OVERCLIP ); + + // don't decrease velocity when going up or down a slope + VectorNormalize(pm->ps->velocity); + VectorScale(pm->ps->velocity, vel, pm->ps->velocity); + + // don't do anything if standing still + if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) { + return; + } + + PM_StepSlideMove( qfalse ); + + //Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity)); + +} + + +/* +============== +PM_DeadMove +============== +*/ +static void PM_DeadMove( void ) { + float forward; + + if ( !pml.walking ) { + return; + } + + // extra friction + + forward = VectorLength (pm->ps->velocity); + forward -= 20; + if ( forward <= 0 ) { + VectorClear (pm->ps->velocity); + } else { + VectorNormalize (pm->ps->velocity); + VectorScale (pm->ps->velocity, forward, pm->ps->velocity); + } +} + + +/* +=============== +PM_NoclipMove +=============== +*/ +static void PM_NoclipMove( void ) { + float speed, drop, friction, control, newspeed; + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + float wishspeed; + float scale; + + pm->ps->viewheight = DEFAULT_VIEWHEIGHT; + + // friction + + speed = VectorLength (pm->ps->velocity); + if (speed < 1) + { + VectorCopy (vec3_origin, pm->ps->velocity); + } + else + { + drop = 0; + + friction = pm_friction*1.5; // extra friction + control = speed < pm_stopspeed ? pm_stopspeed : speed; + drop += control*friction*pml.frametime; + + // scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + newspeed = 0; + newspeed /= speed; + + VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity); + } + + // accelerate + scale = PM_CmdScale( &pm->cmd ); + + fmove = pm->cmd.forwardmove; + smove = pm->cmd.rightmove; + + for (i=0 ; i<3 ; i++) + wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; + wishvel[2] += pm->cmd.upmove; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + wishspeed *= scale; + + PM_Accelerate( wishdir, wishspeed, pm_accelerate ); + + // move + VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin); +} + +//============================================================================ + +/* +================ +PM_FootstepForSurface + +Returns an event number apropriate for the groundsurface +================ +*/ +static int PM_FootstepForSurface( void ) { + if ( pml.groundTrace.surfaceFlags & SURF_NOSTEPS ) { + return 0; + } + if ( pml.groundTrace.surfaceFlags & SURF_METALSTEPS ) { + return EV_FOOTSTEP_METAL; + } + return EV_FOOTSTEP; +} + + +/* +================= +PM_CrashLand + +Check for hard landings that generate sound events +================= +*/ +static void PM_CrashLand( void ) { + float delta; + float dist; + float vel, acc; + float t; + float a, b, c, den; + + // decide which landing animation to use + if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) { + PM_ForceLegsAnim( LEGS_LANDB ); + } else { + PM_ForceLegsAnim( LEGS_LAND ); + } + + pm->ps->legsTimer = TIMER_LAND; + + // calculate the exact velocity on landing + dist = pm->ps->origin[2] - pml.previous_origin[2]; + vel = pml.previous_velocity[2]; + acc = -pm->ps->gravity; + + a = acc / 2; + b = vel; + c = -dist; + + den = b * b - 4 * a * c; + if ( den < 0 ) { + return; + } + t = (-b - sqrt( den ) ) / ( 2 * a ); + + delta = vel + t * acc; + delta = delta*delta * 0.0001; + + // ducking while falling doubles damage + if ( pm->ps->pm_flags & PMF_DUCKED ) { + delta *= 2; + } + + // never take falling damage if completely underwater + if ( pm->waterlevel == 3 ) { + return; + } + + // reduce falling damage if there is standing water + if ( pm->waterlevel == 2 ) { + delta *= 0.25; + } + if ( pm->waterlevel == 1 ) { + delta *= 0.5; + } + + if ( delta < 1 ) { + return; + } + + // create a local entity event to play the sound + + // SURF_NODAMAGE is used for bounce pads where you don't ever + // want to take damage or play a crunch sound + if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) ) { + if ( delta > 60 ) { + PM_AddEvent( EV_FALL_FAR ); + } else if ( delta > 40 ) { + // this is a pain grunt, so don't play it if dead + if ( pm->ps->stats[STAT_HEALTH] > 0 ) { + PM_AddEvent( EV_FALL_MEDIUM ); + } + } else if ( delta > 7 ) { + PM_AddEvent( EV_FALL_SHORT ); + } else { + PM_AddEvent( PM_FootstepForSurface() ); + } + } + + // start footstep cycle over + pm->ps->bobCycle = 0; +} + +/* +============= +PM_CheckStuck +============= +*/ +/* +void PM_CheckStuck(void) { + trace_t trace; + + pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask); + if (trace.allsolid) { + //int shit = qtrue; + } +} +*/ + +/* +============= +PM_CorrectAllSolid +============= +*/ +static int PM_CorrectAllSolid( trace_t *trace ) { + int i, j, k; + vec3_t point; + + if ( pm->debugLevel ) { + Com_Printf("%i:allsolid\n", c_pmove); + } + + // jitter around + for (i = -1; i <= 1; i++) { + for (j = -1; j <= 1; j++) { + for (k = -1; k <= 1; k++) { + VectorCopy(pm->ps->origin, point); + point[0] += (float) i; + point[1] += (float) j; + point[2] += (float) k; + pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); + if ( !trace->allsolid ) { + point[0] = pm->ps->origin[0]; + point[1] = pm->ps->origin[1]; + point[2] = pm->ps->origin[2] - 0.25; + + pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); + pml.groundTrace = *trace; + return qtrue; + } + } + } + } + + pm->ps->groundEntityNum = ENTITYNUM_NONE; + pml.groundPlane = qfalse; + pml.walking = qfalse; + + return qfalse; +} + + +/* +============= +PM_GroundTraceMissed + +The ground trace didn't hit a surface, so we are in freefall +============= +*/ +static void PM_GroundTraceMissed( void ) { + trace_t trace; + vec3_t point; + + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) { + // we just transitioned into freefall + if ( pm->debugLevel ) { + Com_Printf("%i:lift\n", c_pmove); + } + + // if they aren't in a jumping animation and the ground is a ways away, force into it + // if we didn't do the trace, the player would be backflipping down staircases + VectorCopy( pm->ps->origin, point ); + point[2] -= 64; + + pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); + if ( trace.fraction == 1.0 ) { + if ( pm->cmd.forwardmove >= 0 ) { + PM_ForceLegsAnim( LEGS_JUMP ); + pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; + } else { + PM_ForceLegsAnim( LEGS_JUMPB ); + pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; + } + } + } + + pm->ps->groundEntityNum = ENTITYNUM_NONE; + pml.groundPlane = qfalse; + pml.walking = qfalse; +} + + +/* +============= +PM_GroundTrace +============= +*/ +static void PM_GroundTrace( void ) { + vec3_t point; + trace_t trace; + + point[0] = pm->ps->origin[0]; + point[1] = pm->ps->origin[1]; + point[2] = pm->ps->origin[2] - 0.25; + + pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); + pml.groundTrace = trace; + + // do something corrective if the trace starts in a solid... + if ( trace.allsolid ) { + if ( !PM_CorrectAllSolid(&trace) ) + return; + } + + // if the trace didn't hit anything, we are in free fall + if ( trace.fraction == 1.0 ) { + PM_GroundTraceMissed(); + pml.groundPlane = qfalse; + pml.walking = qfalse; + return; + } + + // check if getting thrown off the ground + if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) { + if ( pm->debugLevel ) { + Com_Printf("%i:kickoff\n", c_pmove); + } + // go into jump animation + if ( pm->cmd.forwardmove >= 0 ) { + PM_ForceLegsAnim( LEGS_JUMP ); + pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; + } else { + PM_ForceLegsAnim( LEGS_JUMPB ); + pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; + } + + pm->ps->groundEntityNum = ENTITYNUM_NONE; + pml.groundPlane = qfalse; + pml.walking = qfalse; + return; + } + + // slopes that are too steep will not be considered onground + if ( trace.plane.normal[2] < MIN_WALK_NORMAL ) { + if ( pm->debugLevel ) { + Com_Printf("%i:steep\n", c_pmove); + } + // FIXME: if they can't slide down the slope, let them + // walk (sharp crevices) + pm->ps->groundEntityNum = ENTITYNUM_NONE; + pml.groundPlane = qtrue; + pml.walking = qfalse; + return; + } + + pml.groundPlane = qtrue; + pml.walking = qtrue; + + // hitting solid ground will end a waterjump + if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) + { + pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND); + pm->ps->pm_time = 0; + } + + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) { + // just hit the ground + if ( pm->debugLevel ) { + Com_Printf("%i:Land\n", c_pmove); + } + + PM_CrashLand(); + + // don't do landing time if we were just going down a slope + if ( pml.previous_velocity[2] < -200 ) { + // don't allow another jump for a little while + pm->ps->pm_flags |= PMF_TIME_LAND; + pm->ps->pm_time = 250; + } + } + + pm->ps->groundEntityNum = trace.entityNum; + + // don't reset the z velocity for slopes +// pm->ps->velocity[2] = 0; + + PM_AddTouchEnt( trace.entityNum ); +} + + +/* +============= +PM_SetWaterLevel FIXME: avoid this twice? certainly if not moving +============= +*/ +static void PM_SetWaterLevel( void ) { + vec3_t point; + int cont; + int sample1; + int sample2; + + // + // get waterlevel, accounting for ducking + // + pm->waterlevel = 0; + pm->watertype = 0; + + point[0] = pm->ps->origin[0]; + point[1] = pm->ps->origin[1]; + point[2] = pm->ps->origin[2] + MINS_Z + 1; + cont = pm->pointcontents( point, pm->ps->clientNum ); + + if ( cont & MASK_WATER ) { + sample2 = pm->ps->viewheight - MINS_Z; + sample1 = sample2 / 2; + + pm->watertype = cont; + pm->waterlevel = 1; + point[2] = pm->ps->origin[2] + MINS_Z + sample1; + cont = pm->pointcontents (point, pm->ps->clientNum ); + if ( cont & MASK_WATER ) { + pm->waterlevel = 2; + point[2] = pm->ps->origin[2] + MINS_Z + sample2; + cont = pm->pointcontents (point, pm->ps->clientNum ); + if ( cont & MASK_WATER ){ + pm->waterlevel = 3; + } + } + } + +} + +/* +============== +PM_CheckDuck + +Sets mins, maxs, and pm->ps->viewheight +============== +*/ +static void PM_CheckDuck (void) +{ + trace_t trace; + + if ( pm->ps->powerups[PW_INVULNERABILITY] ) { + if ( pm->ps->pm_flags & PMF_INVULEXPAND ) { + // invulnerability sphere has a 42 units radius + VectorSet( pm->mins, -42, -42, -42 ); + VectorSet( pm->maxs, 42, 42, 42 ); + } + else { + VectorSet( pm->mins, -15, -15, MINS_Z ); + VectorSet( pm->maxs, 15, 15, 16 ); + } + pm->ps->pm_flags |= PMF_DUCKED; + pm->ps->viewheight = CROUCH_VIEWHEIGHT; + return; + } + pm->ps->pm_flags &= ~PMF_INVULEXPAND; + + pm->mins[0] = -15; + pm->mins[1] = -15; + + pm->maxs[0] = 15; + pm->maxs[1] = 15; + + pm->mins[2] = MINS_Z; + + if (pm->ps->pm_type == PM_DEAD) + { + pm->maxs[2] = -8; + pm->ps->viewheight = DEAD_VIEWHEIGHT; + return; + } + + if (pm->cmd.upmove < 0) + { // duck + pm->ps->pm_flags |= PMF_DUCKED; + } + else + { // stand up if possible + if (pm->ps->pm_flags & PMF_DUCKED) + { + // try to stand up + pm->maxs[2] = 32; + pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask ); + if (!trace.allsolid) + pm->ps->pm_flags &= ~PMF_DUCKED; + } + } + + if (pm->ps->pm_flags & PMF_DUCKED) + { + pm->maxs[2] = 16; + pm->ps->viewheight = CROUCH_VIEWHEIGHT; + } + else + { + pm->maxs[2] = 32; + pm->ps->viewheight = DEFAULT_VIEWHEIGHT; + } +} + + + +//=================================================================== + + +/* +=============== +PM_Footsteps +=============== +*/ +static void PM_Footsteps( void ) { + float bobmove; + int old; + qboolean footstep; + + // + // calculate speed and cycle to be used for + // all cyclic walking effects + // + pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0] + + pm->ps->velocity[1] * pm->ps->velocity[1] ); + + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) { + + if ( pm->ps->powerups[PW_INVULNERABILITY] ) { + PM_ContinueLegsAnim( LEGS_IDLECR ); + } + // airborne leaves position in cycle intact, but doesn't advance + if ( pm->waterlevel > 1 ) { + PM_ContinueLegsAnim( LEGS_SWIM ); + } + return; + } + + // if not trying to move + if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) { + if ( pm->xyspeed < 5 ) { + pm->ps->bobCycle = 0; // start at beginning of cycle again + if ( pm->ps->pm_flags & PMF_DUCKED ) { + PM_ContinueLegsAnim( LEGS_IDLECR ); + } else { + PM_ContinueLegsAnim( LEGS_IDLE ); + } + } + return; + } + + + footstep = qfalse; + + if ( pm->ps->pm_flags & PMF_DUCKED ) { + bobmove = 0.5; // ducked characters bob much faster + if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) { + PM_ContinueLegsAnim( LEGS_BACKCR ); + } + else { + PM_ContinueLegsAnim( LEGS_WALKCR ); + } + // ducked characters never play footsteps + /* + } else if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) { + if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) { + bobmove = 0.4; // faster speeds bob faster + footstep = qtrue; + } else { + bobmove = 0.3; + } + PM_ContinueLegsAnim( LEGS_BACK ); + */ + } else { + if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) { + bobmove = 0.4f; // faster speeds bob faster + if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) { + PM_ContinueLegsAnim( LEGS_BACK ); + } + else { + PM_ContinueLegsAnim( LEGS_RUN ); + } + footstep = qtrue; + } else { + bobmove = 0.3f; // walking bobs slow + if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) { + PM_ContinueLegsAnim( LEGS_BACKWALK ); + } + else { + PM_ContinueLegsAnim( LEGS_WALK ); + } + } + } + + // check for footstep / splash sounds + old = pm->ps->bobCycle; + pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255; + + // if we just crossed a cycle boundary, play an apropriate footstep event + if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 ) { + if ( pm->waterlevel == 0 ) { + // on ground will only play sounds if running + if ( footstep && !pm->noFootsteps ) { + PM_AddEvent( PM_FootstepForSurface() ); + } + } else if ( pm->waterlevel == 1 ) { + // splashing + PM_AddEvent( EV_FOOTSPLASH ); + } else if ( pm->waterlevel == 2 ) { + // wading / swimming at surface + PM_AddEvent( EV_SWIM ); + } else if ( pm->waterlevel == 3 ) { + // no sound when completely underwater + + } + } +} + +/* +============== +PM_WaterEvents + +Generate sound events for entering and leaving water +============== +*/ +static void PM_WaterEvents( void ) { // FIXME? + // + // if just entered a water volume, play a sound + // + if (!pml.previous_waterlevel && pm->waterlevel) { + PM_AddEvent( EV_WATER_TOUCH ); + } + + // + // if just completely exited a water volume, play a sound + // + if (pml.previous_waterlevel && !pm->waterlevel) { + PM_AddEvent( EV_WATER_LEAVE ); + } + + // + // check for head just going under water + // + if (pml.previous_waterlevel != 3 && pm->waterlevel == 3) { + PM_AddEvent( EV_WATER_UNDER ); + } + + // + // check for head just coming out of water + // + if (pml.previous_waterlevel == 3 && pm->waterlevel != 3) { + PM_AddEvent( EV_WATER_CLEAR ); + } +} + + +/* +=============== +PM_BeginWeaponChange +=============== +*/ +static void PM_BeginWeaponChange( int weapon ) { + if ( weapon <= WP_NONE || weapon >= WP_NUM_WEAPONS ) { + return; + } + + if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) { + return; + } + + if ( pm->ps->weaponstate == WEAPON_DROPPING ) { + return; + } + + PM_AddEvent( EV_CHANGE_WEAPON ); + pm->ps->weaponstate = WEAPON_DROPPING; + pm->ps->weaponTime += 200; + PM_StartTorsoAnim( TORSO_DROP ); +} + + +/* +=============== +PM_FinishWeaponChange +=============== +*/ +static void PM_FinishWeaponChange( void ) { + int weapon; + + weapon = pm->cmd.weapon; + if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) { + weapon = WP_NONE; + } + + if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) { + weapon = WP_NONE; + } + + pm->ps->weapon = weapon; + pm->ps->weaponstate = WEAPON_RAISING; + pm->ps->weaponTime += 250; + PM_StartTorsoAnim( TORSO_RAISE ); +} + + +/* +============== +PM_TorsoAnimation + +============== +*/ +static void PM_TorsoAnimation( void ) { + if ( pm->ps->weaponstate == WEAPON_READY ) { + if ( pm->ps->weapon == WP_GAUNTLET ) { + PM_ContinueTorsoAnim( TORSO_STAND2 ); + } else { + PM_ContinueTorsoAnim( TORSO_STAND ); + } + return; + } +} + + +/* +============== +PM_Weapon + +Generates weapon events and modifes the weapon counter +============== +*/ +static void PM_Weapon( void ) { + int addTime; + + // don't allow attack until all buttons are up + if ( pm->ps->pm_flags & PMF_RESPAWNED ) { + return; + } + + // ignore if spectator + if ( pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) { + return; + } + + // check for dead player + if ( pm->ps->stats[STAT_HEALTH] <= 0 ) { + pm->ps->weapon = WP_NONE; + return; + } + + // check for item using + if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) { + if ( ! ( pm->ps->pm_flags & PMF_USE_ITEM_HELD ) ) { + if ( bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag == HI_MEDKIT + && pm->ps->stats[STAT_HEALTH] >= (pm->ps->stats[STAT_MAX_HEALTH] + 25) ) { + // don't use medkit if at max health + } else { + pm->ps->pm_flags |= PMF_USE_ITEM_HELD; + PM_AddEvent( EV_USE_ITEM0 + bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag ); + pm->ps->stats[STAT_HOLDABLE_ITEM] = 0; + } + return; + } + } else { + pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD; + } + + + // make weapon function + if ( pm->ps->weaponTime > 0 ) { + pm->ps->weaponTime -= pml.msec; + } + + // check for weapon change + // can't change if weapon is firing, but can change + // again if lowering or raising + if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) { + if ( pm->ps->weapon != pm->cmd.weapon ) { + PM_BeginWeaponChange( pm->cmd.weapon ); + } + } + + if ( pm->ps->weaponTime > 0 ) { + return; + } + + // change weapon if time + if ( pm->ps->weaponstate == WEAPON_DROPPING ) { + PM_FinishWeaponChange(); + return; + } + + if ( pm->ps->weaponstate == WEAPON_RAISING ) { + pm->ps->weaponstate = WEAPON_READY; + if ( pm->ps->weapon == WP_GAUNTLET ) { + PM_StartTorsoAnim( TORSO_STAND2 ); + } else { + PM_StartTorsoAnim( TORSO_STAND ); + } + return; + } + + // check for fire + if ( ! (pm->cmd.buttons & BUTTON_ATTACK) ) { + pm->ps->weaponTime = 0; + pm->ps->weaponstate = WEAPON_READY; + return; + } + + // start the animation even if out of ammo + if ( pm->ps->weapon == WP_GAUNTLET ) { + // the guantlet only "fires" when it actually hits something + if ( !pm->gauntletHit ) { + pm->ps->weaponTime = 0; + pm->ps->weaponstate = WEAPON_READY; + return; + } + PM_StartTorsoAnim( TORSO_ATTACK2 ); + } else { + PM_StartTorsoAnim( TORSO_ATTACK ); + } + + pm->ps->weaponstate = WEAPON_FIRING; + + // check for out of ammo + if ( ! pm->ps->ammo[ pm->ps->weapon ] ) { + PM_AddEvent( EV_NOAMMO ); + pm->ps->weaponTime += 500; + return; + } + + // take an ammo away if not infinite + if ( pm->ps->ammo[ pm->ps->weapon ] != -1 ) { + pm->ps->ammo[ pm->ps->weapon ]--; + } + + // fire weapon + PM_AddEvent( EV_FIRE_WEAPON ); + + switch( pm->ps->weapon ) { + default: + case WP_GAUNTLET: + addTime = 400; + break; + case WP_LIGHTNING: + addTime = 50; + break; + case WP_SHOTGUN: + addTime = 1000; + break; + case WP_MACHINEGUN: + addTime = 100; + break; + case WP_GRENADE_LAUNCHER: + addTime = 800; + break; + case WP_ROCKET_LAUNCHER: + addTime = 800; + break; + case WP_PLASMAGUN: + addTime = 100; + break; + case WP_RAILGUN: + addTime = 1500; + break; + case WP_BFG: + addTime = 200; + break; + case WP_GRAPPLING_HOOK: + addTime = 400; + break; +#ifdef MISSIONPACK + case WP_NAILGUN: + addTime = 1000; + break; + case WP_PROX_LAUNCHER: + addTime = 800; + break; + case WP_CHAINGUN: + addTime = 30; + break; +#endif + } + +#ifdef MISSIONPACK + if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) { + addTime /= 1.5; + } + else + if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) { + addTime /= 1.3; + } + else +#endif + if ( pm->ps->powerups[PW_HASTE] ) { + addTime /= 1.3; + } + + pm->ps->weaponTime += addTime; +} + +/* +================ +PM_Animate +================ +*/ + +static void PM_Animate( void ) { + if ( pm->cmd.buttons & BUTTON_GESTURE ) { + if ( pm->ps->torsoTimer == 0 ) { + PM_StartTorsoAnim( TORSO_GESTURE ); + pm->ps->torsoTimer = TIMER_GESTURE; + PM_AddEvent( EV_TAUNT ); + } +#ifdef MISSIONPACK + } else if ( pm->cmd.buttons & BUTTON_GETFLAG ) { + if ( pm->ps->torsoTimer == 0 ) { + PM_StartTorsoAnim( TORSO_GETFLAG ); + pm->ps->torsoTimer = 600; //TIMER_GESTURE; + } + } else if ( pm->cmd.buttons & BUTTON_GUARDBASE ) { + if ( pm->ps->torsoTimer == 0 ) { + PM_StartTorsoAnim( TORSO_GUARDBASE ); + pm->ps->torsoTimer = 600; //TIMER_GESTURE; + } + } else if ( pm->cmd.buttons & BUTTON_PATROL ) { + if ( pm->ps->torsoTimer == 0 ) { + PM_StartTorsoAnim( TORSO_PATROL ); + pm->ps->torsoTimer = 600; //TIMER_GESTURE; + } + } else if ( pm->cmd.buttons & BUTTON_FOLLOWME ) { + if ( pm->ps->torsoTimer == 0 ) { + PM_StartTorsoAnim( TORSO_FOLLOWME ); + pm->ps->torsoTimer = 600; //TIMER_GESTURE; + } + } else if ( pm->cmd.buttons & BUTTON_AFFIRMATIVE ) { + if ( pm->ps->torsoTimer == 0 ) { + PM_StartTorsoAnim( TORSO_AFFIRMATIVE); + pm->ps->torsoTimer = 600; //TIMER_GESTURE; + } + } else if ( pm->cmd.buttons & BUTTON_NEGATIVE ) { + if ( pm->ps->torsoTimer == 0 ) { + PM_StartTorsoAnim( TORSO_NEGATIVE ); + pm->ps->torsoTimer = 600; //TIMER_GESTURE; + } +#endif + } +} + + +/* +================ +PM_DropTimers +================ +*/ +static void PM_DropTimers( void ) { + // drop misc timing counter + if ( pm->ps->pm_time ) { + if ( pml.msec >= pm->ps->pm_time ) { + pm->ps->pm_flags &= ~PMF_ALL_TIMES; + pm->ps->pm_time = 0; + } else { + pm->ps->pm_time -= pml.msec; + } + } + + // drop animation counter + if ( pm->ps->legsTimer > 0 ) { + pm->ps->legsTimer -= pml.msec; + if ( pm->ps->legsTimer < 0 ) { + pm->ps->legsTimer = 0; + } + } + + if ( pm->ps->torsoTimer > 0 ) { + pm->ps->torsoTimer -= pml.msec; + if ( pm->ps->torsoTimer < 0 ) { + pm->ps->torsoTimer = 0; + } + } +} + +/* +================ +PM_UpdateViewAngles + +This can be used as another entry point when only the viewangles +are being updated isntead of a full move +================ +*/ +void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) { + short temp; + int i; + + if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) { + return; // no view changes at all + } + + if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) { + return; // no view changes at all + } + + // circularly clamp the angles with deltas + for (i=0 ; i<3 ; i++) { + temp = cmd->angles[i] + ps->delta_angles[i]; + if ( i == PITCH ) { + // don't let the player look up or down more than 90 degrees + if ( temp > 16000 ) { + ps->delta_angles[i] = 16000 - cmd->angles[i]; + temp = 16000; + } else if ( temp < -16000 ) { + ps->delta_angles[i] = -16000 - cmd->angles[i]; + temp = -16000; + } + } + ps->viewangles[i] = SHORT2ANGLE(temp); + } + +} + + +/* +================ +PmoveSingle + +================ +*/ +void trap_SnapVector( float *v ); + +void PmoveSingle (pmove_t *pmove) { + pm = pmove; + + // this counter lets us debug movement problems with a journal + // by setting a conditional breakpoint fot the previous frame + c_pmove++; + + // clear results + pm->numtouch = 0; + pm->watertype = 0; + pm->waterlevel = 0; + + if ( pm->ps->stats[STAT_HEALTH] <= 0 ) { + pm->tracemask &= ~CONTENTS_BODY; // corpses can fly through bodies + } + + // make sure walking button is clear if they are running, to avoid + // proxy no-footsteps cheats + if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) { + pm->cmd.buttons &= ~BUTTON_WALKING; + } + + // set the talk balloon flag + if ( pm->cmd.buttons & BUTTON_TALK ) { + pm->ps->eFlags |= EF_TALK; + } else { + pm->ps->eFlags &= ~EF_TALK; + } + + // set the firing flag for continuous beam weapons + if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION + && ( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->ammo[ pm->ps->weapon ] ) { + pm->ps->eFlags |= EF_FIRING; + } else { + pm->ps->eFlags &= ~EF_FIRING; + } + + // clear the respawned flag if attack and use are cleared + if ( pm->ps->stats[STAT_HEALTH] > 0 && + !( pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE) ) ) { + pm->ps->pm_flags &= ~PMF_RESPAWNED; + } + + // if talk button is down, dissallow all other input + // this is to prevent any possible intercept proxy from + // adding fake talk balloons + if ( pmove->cmd.buttons & BUTTON_TALK ) { + // keep the talk button set tho for when the cmd.serverTime > 66 msec + // and the same cmd is used multiple times in Pmove + pmove->cmd.buttons = BUTTON_TALK; + pmove->cmd.forwardmove = 0; + pmove->cmd.rightmove = 0; + pmove->cmd.upmove = 0; + } + + // clear all pmove local vars + memset (&pml, 0, sizeof(pml)); + + // determine the time + pml.msec = pmove->cmd.serverTime - pm->ps->commandTime; + if ( pml.msec < 1 ) { + pml.msec = 1; + } else if ( pml.msec > 200 ) { + pml.msec = 200; + } + pm->ps->commandTime = pmove->cmd.serverTime; + + // save old org in case we get stuck + VectorCopy (pm->ps->origin, pml.previous_origin); + + // save old velocity for crashlanding + VectorCopy (pm->ps->velocity, pml.previous_velocity); + + pml.frametime = pml.msec * 0.001; + + // update the viewangles + PM_UpdateViewAngles( pm->ps, &pm->cmd ); + + AngleVectors (pm->ps->viewangles, pml.forward, pml.right, pml.up); + + if ( pm->cmd.upmove < 10 ) { + // not holding jump + pm->ps->pm_flags &= ~PMF_JUMP_HELD; + } + + // decide if backpedaling animations should be used + if ( pm->cmd.forwardmove < 0 ) { + pm->ps->pm_flags |= PMF_BACKWARDS_RUN; + } else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) { + pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN; + } + + if ( pm->ps->pm_type >= PM_DEAD ) { + pm->cmd.forwardmove = 0; + pm->cmd.rightmove = 0; + pm->cmd.upmove = 0; + } + + if ( pm->ps->pm_type == PM_SPECTATOR ) { + PM_CheckDuck (); + PM_FlyMove (); + PM_DropTimers (); + return; + } + + if ( pm->ps->pm_type == PM_NOCLIP ) { + PM_NoclipMove (); + PM_DropTimers (); + return; + } + + if (pm->ps->pm_type == PM_FREEZE) { + return; // no movement at all + } + + if ( pm->ps->pm_type == PM_INTERMISSION || pm->ps->pm_type == PM_SPINTERMISSION) { + return; // no movement at all + } + + // set watertype, and waterlevel + PM_SetWaterLevel(); + pml.previous_waterlevel = pmove->waterlevel; + + // set mins, maxs, and viewheight + PM_CheckDuck (); + + // set groundentity + PM_GroundTrace(); + + if ( pm->ps->pm_type == PM_DEAD ) { + PM_DeadMove (); + } + + PM_DropTimers(); + +#ifdef MISSIONPACK + if ( pm->ps->powerups[PW_INVULNERABILITY] ) { + PM_InvulnerabilityMove(); + } else +#endif + if ( pm->ps->powerups[PW_FLIGHT] ) { + // flight powerup doesn't allow jump and has different friction + PM_FlyMove(); + } else if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) { + PM_GrappleMove(); + // We can wiggle a bit + PM_AirMove(); + } else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) { + PM_WaterJumpMove(); + } else if ( pm->waterlevel > 1 ) { + // swimming + PM_WaterMove(); + } else if ( pml.walking ) { + // walking on ground + PM_WalkMove(); + } else { + // airborne + PM_AirMove(); + } + + PM_Animate(); + + // set groundentity, watertype, and waterlevel + PM_GroundTrace(); + PM_SetWaterLevel(); + + // weapons + PM_Weapon(); + + // torso animation + PM_TorsoAnimation(); + + // footstep events / legs animations + PM_Footsteps(); + + // entering / leaving water splashes + PM_WaterEvents(); + + // snap some parts of playerstate to save network bandwidth + trap_SnapVector( pm->ps->velocity ); +} + + +/* +================ +Pmove + +Can be called by either the server or the client +================ +*/ +void Pmove (pmove_t *pmove) { + int finalTime; + + finalTime = pmove->cmd.serverTime; + + if ( finalTime < pmove->ps->commandTime ) { + return; // should not happen + } + + if ( finalTime > pmove->ps->commandTime + 1000 ) { + pmove->ps->commandTime = finalTime - 1000; + } + + pmove->ps->pmove_framecount = (pmove->ps->pmove_framecount+1) & ((1<ps->commandTime != finalTime ) { + int msec; + + msec = finalTime - pmove->ps->commandTime; + + if ( pmove->pmove_fixed ) { + if ( msec > pmove->pmove_msec ) { + msec = pmove->pmove_msec; + } + } + else { + if ( msec > 66 ) { + msec = 66; + } + } + pmove->cmd.serverTime = pmove->ps->commandTime + msec; + PmoveSingle( pmove ); + + if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) { + pmove->cmd.upmove = 20; + } + } + + //PM_CheckStuck(); + +} + diff --git a/reaction/engine/code/game/bg_public.h b/reaction/engine/code/game/bg_public.h new file mode 100644 index 00000000..ad5ab39e --- /dev/null +++ b/reaction/engine/code/game/bg_public.h @@ -0,0 +1,738 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// bg_public.h -- definitions shared by both the server game and client game modules + +// because games can change separately from the main system version, we need a +// second version that must match between game and cgame + +#define GAME_VERSION BASEGAME "-1" + +#define DEFAULT_GRAVITY 800 +#define GIB_HEALTH -40 +#define ARMOR_PROTECTION 0.66 + +#define MAX_ITEMS 256 + +#define RANK_TIED_FLAG 0x4000 + +#define DEFAULT_SHOTGUN_SPREAD 700 +#define DEFAULT_SHOTGUN_COUNT 11 + +#define ITEM_RADIUS 15 // item sizes are needed for client side pickup detection + +#define LIGHTNING_RANGE 768 + +#define SCORE_NOT_PRESENT -9999 // for the CS_SCORES[12] when only one player is present + +#define VOTE_TIME 30000 // 30 seconds before vote times out + +#define MINS_Z -24 +#define DEFAULT_VIEWHEIGHT 26 +#define CROUCH_VIEWHEIGHT 12 +#define DEAD_VIEWHEIGHT -16 + +// +// config strings are a general means of communicating variable length strings +// from the server to all connected clients. +// + +// CS_SERVERINFO and CS_SYSTEMINFO are defined in q_shared.h +#define CS_MUSIC 2 +#define CS_MESSAGE 3 // from the map worldspawn's message field +#define CS_MOTD 4 // g_motd string for server message of the day +#define CS_WARMUP 5 // server time when the match will be restarted +#define CS_SCORES1 6 +#define CS_SCORES2 7 +#define CS_VOTE_TIME 8 +#define CS_VOTE_STRING 9 +#define CS_VOTE_YES 10 +#define CS_VOTE_NO 11 + +#define CS_TEAMVOTE_TIME 12 +#define CS_TEAMVOTE_STRING 14 +#define CS_TEAMVOTE_YES 16 +#define CS_TEAMVOTE_NO 18 + +#define CS_GAME_VERSION 20 +#define CS_LEVEL_START_TIME 21 // so the timer only shows the current level +#define CS_INTERMISSION 22 // when 1, fraglimit/timelimit has been hit and intermission will start in a second or two +#define CS_FLAGSTATUS 23 // string indicating flag status in CTF +#define CS_SHADERSTATE 24 +#define CS_BOTINFO 25 + +#define CS_ITEMS 27 // string of 0's and 1's that tell which items are present + +#define CS_MODELS 32 +#define CS_SOUNDS (CS_MODELS+MAX_MODELS) +#define CS_PLAYERS (CS_SOUNDS+MAX_SOUNDS) +#define CS_LOCATIONS (CS_PLAYERS+MAX_CLIENTS) +#define CS_PARTICLES (CS_LOCATIONS+MAX_LOCATIONS) + +#define CS_MAX (CS_PARTICLES+MAX_LOCATIONS) + +#if (CS_MAX) > MAX_CONFIGSTRINGS +#error overflow: (CS_MAX) > MAX_CONFIGSTRINGS +#endif + +typedef enum { + GT_FFA, // free for all + GT_TOURNAMENT, // one on one tournament + GT_SINGLE_PLAYER, // single player ffa + + //-- team games go after this -- + + GT_TEAM, // team deathmatch + GT_CTF, // capture the flag + GT_1FCTF, + GT_OBELISK, + GT_HARVESTER, + GT_MAX_GAME_TYPE +} gametype_t; + +typedef enum { GENDER_MALE, GENDER_FEMALE, GENDER_NEUTER } gender_t; + +/* +=================================================================================== + +PMOVE MODULE + +The pmove code takes a player_state_t and a usercmd_t and generates a new player_state_t +and some other output data. Used for local prediction on the client game and true +movement on the server game. +=================================================================================== +*/ + +typedef enum { + PM_NORMAL, // can accelerate and turn + PM_NOCLIP, // noclip movement + PM_SPECTATOR, // still run into walls + PM_DEAD, // no acceleration or turning, but free falling + PM_FREEZE, // stuck in place with no control + PM_INTERMISSION, // no movement or status bar + PM_SPINTERMISSION // no movement or status bar +} pmtype_t; + +typedef enum { + WEAPON_READY, + WEAPON_RAISING, + WEAPON_DROPPING, + WEAPON_FIRING +} weaponstate_t; + +// pmove->pm_flags +#define PMF_DUCKED 1 +#define PMF_JUMP_HELD 2 +#define PMF_BACKWARDS_JUMP 8 // go into backwards land +#define PMF_BACKWARDS_RUN 16 // coast down to backwards run +#define PMF_TIME_LAND 32 // pm_time is time before rejump +#define PMF_TIME_KNOCKBACK 64 // pm_time is an air-accelerate only time +#define PMF_TIME_WATERJUMP 256 // pm_time is waterjump +#define PMF_RESPAWNED 512 // clear after attack and jump buttons come up +#define PMF_USE_ITEM_HELD 1024 +#define PMF_GRAPPLE_PULL 2048 // pull towards grapple location +#define PMF_FOLLOW 4096 // spectate following another player +#define PMF_SCOREBOARD 8192 // spectate as a scoreboard +#define PMF_INVULEXPAND 16384 // invulnerability sphere set to full size + +#define PMF_ALL_TIMES (PMF_TIME_WATERJUMP|PMF_TIME_LAND|PMF_TIME_KNOCKBACK) + +#define MAXTOUCH 32 +typedef struct { + // state (in / out) + playerState_t *ps; + + // command (in) + usercmd_t cmd; + int tracemask; // collide against these types of surfaces + int debugLevel; // if set, diagnostic output will be printed + qboolean noFootsteps; // if the game is setup for no footsteps by the server + qboolean gauntletHit; // true if a gauntlet attack would actually hit something + + int framecount; + + // results (out) + int numtouch; + int touchents[MAXTOUCH]; + + vec3_t mins, maxs; // bounding box size + + int watertype; + int waterlevel; + + float xyspeed; + + // for fixed msec Pmove + int pmove_fixed; + int pmove_msec; + + // callbacks to test the world + // these will be different functions during game and cgame + void (*trace)( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask ); + int (*pointcontents)( const vec3_t point, int passEntityNum ); +} pmove_t; + +// if a full pmove isn't done on the client, you can just update the angles +void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ); +void Pmove (pmove_t *pmove); + +//=================================================================================== + + +// player_state->stats[] indexes +// NOTE: may not have more than 16 +typedef enum { + STAT_HEALTH, + STAT_HOLDABLE_ITEM, +#ifdef MISSIONPACK + STAT_PERSISTANT_POWERUP, +#endif + STAT_WEAPONS, // 16 bit fields + STAT_ARMOR, + STAT_DEAD_YAW, // look this direction when dead (FIXME: get rid of?) + STAT_CLIENTS_READY, // bit mask of clients wishing to exit the intermission (FIXME: configstring?) + STAT_MAX_HEALTH // health / armor limit, changable by handicap +} statIndex_t; + + +// player_state->persistant[] indexes +// these fields are the only part of player_state that isn't +// cleared on respawn +// NOTE: may not have more than 16 +typedef enum { + PERS_SCORE, // !!! MUST NOT CHANGE, SERVER AND GAME BOTH REFERENCE !!! + PERS_HITS, // total points damage inflicted so damage beeps can sound on change + PERS_RANK, // player rank or team rank + PERS_TEAM, // player team + PERS_SPAWN_COUNT, // incremented every respawn + PERS_PLAYEREVENTS, // 16 bits that can be flipped for events + PERS_ATTACKER, // clientnum of last damage inflicter + PERS_ATTACKEE_ARMOR, // health/armor of last person we attacked + PERS_KILLED, // count of the number of times you died + // player awards tracking + PERS_IMPRESSIVE_COUNT, // two railgun hits in a row + PERS_EXCELLENT_COUNT, // two successive kills in a short amount of time + PERS_DEFEND_COUNT, // defend awards + PERS_ASSIST_COUNT, // assist awards + PERS_GAUNTLET_FRAG_COUNT, // kills with the guantlet + PERS_CAPTURES // captures +} persEnum_t; + + +// entityState_t->eFlags +#define EF_DEAD 0x00000001 // don't draw a foe marker over players with EF_DEAD +#ifdef MISSIONPACK +#define EF_TICKING 0x00000002 // used to make players play the prox mine ticking sound +#endif +#define EF_TELEPORT_BIT 0x00000004 // toggled every time the origin abruptly changes +#define EF_AWARD_EXCELLENT 0x00000008 // draw an excellent sprite +#define EF_PLAYER_EVENT 0x00000010 +#define EF_BOUNCE 0x00000010 // for missiles +#define EF_BOUNCE_HALF 0x00000020 // for missiles +#define EF_AWARD_GAUNTLET 0x00000040 // draw a gauntlet sprite +#define EF_NODRAW 0x00000080 // may have an event, but no model (unspawned items) +#define EF_FIRING 0x00000100 // for lightning gun +#define EF_KAMIKAZE 0x00000200 +#define EF_MOVER_STOP 0x00000400 // will push otherwise +#define EF_AWARD_CAP 0x00000800 // draw the capture sprite +#define EF_TALK 0x00001000 // draw a talk balloon +#define EF_CONNECTION 0x00002000 // draw a connection trouble sprite +#define EF_VOTED 0x00004000 // already cast a vote +#define EF_AWARD_IMPRESSIVE 0x00008000 // draw an impressive sprite +#define EF_AWARD_DEFEND 0x00010000 // draw a defend sprite +#define EF_AWARD_ASSIST 0x00020000 // draw a assist sprite +#define EF_AWARD_DENIED 0x00040000 // denied +#define EF_TEAMVOTED 0x00080000 // already cast a team vote + +// NOTE: may not have more than 16 +typedef enum { + PW_NONE, + + PW_QUAD, + PW_BATTLESUIT, + PW_HASTE, + PW_INVIS, + PW_REGEN, + PW_FLIGHT, + + PW_REDFLAG, + PW_BLUEFLAG, + PW_NEUTRALFLAG, + + PW_SCOUT, + PW_GUARD, + PW_DOUBLER, + PW_AMMOREGEN, + PW_INVULNERABILITY, + + PW_NUM_POWERUPS + +} powerup_t; + +typedef enum { + HI_NONE, + + HI_TELEPORTER, + HI_MEDKIT, + HI_KAMIKAZE, + HI_PORTAL, + HI_INVULNERABILITY, + + HI_NUM_HOLDABLE +} holdable_t; + + +typedef enum { + WP_NONE, + + WP_GAUNTLET, + WP_MACHINEGUN, + WP_SHOTGUN, + WP_GRENADE_LAUNCHER, + WP_ROCKET_LAUNCHER, + WP_LIGHTNING, + WP_RAILGUN, + WP_PLASMAGUN, + WP_BFG, + WP_GRAPPLING_HOOK, +#ifdef MISSIONPACK + WP_NAILGUN, + WP_PROX_LAUNCHER, + WP_CHAINGUN, +#endif + + WP_NUM_WEAPONS +} weapon_t; + + +// reward sounds (stored in ps->persistant[PERS_PLAYEREVENTS]) +#define PLAYEREVENT_DENIEDREWARD 0x0001 +#define PLAYEREVENT_GAUNTLETREWARD 0x0002 +#define PLAYEREVENT_HOLYSHIT 0x0004 + +// entityState_t->event values +// entity events are for effects that take place reletive +// to an existing entities origin. Very network efficient. + +// two bits at the top of the entityState->event field +// will be incremented with each change in the event so +// that an identical event started twice in a row can +// be distinguished. And off the value with ~EV_EVENT_BITS +// to retrieve the actual event number +#define EV_EVENT_BIT1 0x00000100 +#define EV_EVENT_BIT2 0x00000200 +#define EV_EVENT_BITS (EV_EVENT_BIT1|EV_EVENT_BIT2) + +#define EVENT_VALID_MSEC 300 + +typedef enum { + EV_NONE, + + EV_FOOTSTEP, + EV_FOOTSTEP_METAL, + EV_FOOTSPLASH, + EV_FOOTWADE, + EV_SWIM, + + EV_STEP_4, + EV_STEP_8, + EV_STEP_12, + EV_STEP_16, + + EV_FALL_SHORT, + EV_FALL_MEDIUM, + EV_FALL_FAR, + + EV_JUMP_PAD, // boing sound at origin, jump sound on player + + EV_JUMP, + EV_WATER_TOUCH, // foot touches + EV_WATER_LEAVE, // foot leaves + EV_WATER_UNDER, // head touches + EV_WATER_CLEAR, // head leaves + + EV_ITEM_PICKUP, // normal item pickups are predictable + EV_GLOBAL_ITEM_PICKUP, // powerup / team sounds are broadcast to everyone + + EV_NOAMMO, + EV_CHANGE_WEAPON, + EV_FIRE_WEAPON, + + EV_USE_ITEM0, + EV_USE_ITEM1, + EV_USE_ITEM2, + EV_USE_ITEM3, + EV_USE_ITEM4, + EV_USE_ITEM5, + EV_USE_ITEM6, + EV_USE_ITEM7, + EV_USE_ITEM8, + EV_USE_ITEM9, + EV_USE_ITEM10, + EV_USE_ITEM11, + EV_USE_ITEM12, + EV_USE_ITEM13, + EV_USE_ITEM14, + EV_USE_ITEM15, + + EV_ITEM_RESPAWN, + EV_ITEM_POP, + EV_PLAYER_TELEPORT_IN, + EV_PLAYER_TELEPORT_OUT, + + EV_GRENADE_BOUNCE, // eventParm will be the soundindex + + EV_GENERAL_SOUND, + EV_GLOBAL_SOUND, // no attenuation + EV_GLOBAL_TEAM_SOUND, + + EV_BULLET_HIT_FLESH, + EV_BULLET_HIT_WALL, + + EV_MISSILE_HIT, + EV_MISSILE_MISS, + EV_MISSILE_MISS_METAL, + EV_RAILTRAIL, + EV_SHOTGUN, + EV_BULLET, // otherEntity is the shooter + + EV_PAIN, + EV_DEATH1, + EV_DEATH2, + EV_DEATH3, + EV_OBITUARY, + + EV_POWERUP_QUAD, + EV_POWERUP_BATTLESUIT, + EV_POWERUP_REGEN, + + EV_GIB_PLAYER, // gib a previously living player + EV_SCOREPLUM, // score plum + +//#ifdef MISSIONPACK + EV_PROXIMITY_MINE_STICK, + EV_PROXIMITY_MINE_TRIGGER, + EV_KAMIKAZE, // kamikaze explodes + EV_OBELISKEXPLODE, // obelisk explodes + EV_OBELISKPAIN, // obelisk is in pain + EV_INVUL_IMPACT, // invulnerability sphere impact + EV_JUICED, // invulnerability juiced effect + EV_LIGHTNINGBOLT, // lightning bolt bounced of invulnerability sphere +//#endif + + EV_DEBUG_LINE, + EV_STOPLOOPINGSOUND, + EV_TAUNT, + EV_TAUNT_YES, + EV_TAUNT_NO, + EV_TAUNT_FOLLOWME, + EV_TAUNT_GETFLAG, + EV_TAUNT_GUARDBASE, + EV_TAUNT_PATROL + +} entity_event_t; + + +typedef enum { + GTS_RED_CAPTURE, + GTS_BLUE_CAPTURE, + GTS_RED_RETURN, + GTS_BLUE_RETURN, + GTS_RED_TAKEN, + GTS_BLUE_TAKEN, + GTS_REDOBELISK_ATTACKED, + GTS_BLUEOBELISK_ATTACKED, + GTS_REDTEAM_SCORED, + GTS_BLUETEAM_SCORED, + GTS_REDTEAM_TOOK_LEAD, + GTS_BLUETEAM_TOOK_LEAD, + GTS_TEAMS_ARE_TIED, + GTS_KAMIKAZE +} global_team_sound_t; + +// animations +typedef enum { + BOTH_DEATH1, + BOTH_DEAD1, + BOTH_DEATH2, + BOTH_DEAD2, + BOTH_DEATH3, + BOTH_DEAD3, + + TORSO_GESTURE, + + TORSO_ATTACK, + TORSO_ATTACK2, + + TORSO_DROP, + TORSO_RAISE, + + TORSO_STAND, + TORSO_STAND2, + + LEGS_WALKCR, + LEGS_WALK, + LEGS_RUN, + LEGS_BACK, + LEGS_SWIM, + + LEGS_JUMP, + LEGS_LAND, + + LEGS_JUMPB, + LEGS_LANDB, + + LEGS_IDLE, + LEGS_IDLECR, + + LEGS_TURN, + + TORSO_GETFLAG, + TORSO_GUARDBASE, + TORSO_PATROL, + TORSO_FOLLOWME, + TORSO_AFFIRMATIVE, + TORSO_NEGATIVE, + + MAX_ANIMATIONS, + + LEGS_BACKCR, + LEGS_BACKWALK, + FLAG_RUN, + FLAG_STAND, + FLAG_STAND2RUN, + + MAX_TOTALANIMATIONS +} animNumber_t; + + +typedef struct animation_s { + int firstFrame; + int numFrames; + int loopFrames; // 0 to numFrames + int frameLerp; // msec between frames + int initialLerp; // msec to get to first frame + int reversed; // true if animation is reversed + int flipflop; // true if animation should flipflop back to base +} animation_t; + + +// flip the togglebit every time an animation +// changes so a restart of the same anim can be detected +#define ANIM_TOGGLEBIT 128 + + +typedef enum { + TEAM_FREE, + TEAM_RED, + TEAM_BLUE, + TEAM_SPECTATOR, + + TEAM_NUM_TEAMS +} team_t; + +// Time between location updates +#define TEAM_LOCATION_UPDATE_TIME 1000 + +// How many players on the overlay +#define TEAM_MAXOVERLAY 32 + +//team task +typedef enum { + TEAMTASK_NONE, + TEAMTASK_OFFENSE, + TEAMTASK_DEFENSE, + TEAMTASK_PATROL, + TEAMTASK_FOLLOW, + TEAMTASK_RETRIEVE, + TEAMTASK_ESCORT, + TEAMTASK_CAMP +} teamtask_t; + +// means of death +typedef enum { + MOD_UNKNOWN, + MOD_SHOTGUN, + MOD_GAUNTLET, + MOD_MACHINEGUN, + MOD_GRENADE, + MOD_GRENADE_SPLASH, + MOD_ROCKET, + MOD_ROCKET_SPLASH, + MOD_PLASMA, + MOD_PLASMA_SPLASH, + MOD_RAILGUN, + MOD_LIGHTNING, + MOD_BFG, + MOD_BFG_SPLASH, + MOD_WATER, + MOD_SLIME, + MOD_LAVA, + MOD_CRUSH, + MOD_TELEFRAG, + MOD_FALLING, + MOD_SUICIDE, + MOD_TARGET_LASER, + MOD_TRIGGER_HURT, +#ifdef MISSIONPACK + MOD_NAIL, + MOD_CHAINGUN, + MOD_PROXIMITY_MINE, + MOD_KAMIKAZE, + MOD_JUICED, +#endif + MOD_GRAPPLE +} meansOfDeath_t; + + +//--------------------------------------------------------- + +// gitem_t->type +typedef enum { + IT_BAD, + IT_WEAPON, // EFX: rotate + upscale + minlight + IT_AMMO, // EFX: rotate + IT_ARMOR, // EFX: rotate + minlight + IT_HEALTH, // EFX: static external sphere + rotating internal + IT_POWERUP, // instant on, timer based + // EFX: rotate + external ring that rotates + IT_HOLDABLE, // single use, holdable item + // EFX: rotate + bob + IT_PERSISTANT_POWERUP, + IT_TEAM +} itemType_t; + +#define MAX_ITEM_MODELS 4 + +typedef struct gitem_s { + char *classname; // spawning name + char *pickup_sound; + char *world_model[MAX_ITEM_MODELS]; + + char *icon; + char *pickup_name; // for printing on pickup + + int quantity; // for ammo how much, or duration of powerup + itemType_t giType; // IT_* flags + + int giTag; + + char *precaches; // string of all models and images this item will use + char *sounds; // string of all sounds this item will use +} gitem_t; + +// included in both the game dll and the client +extern gitem_t bg_itemlist[]; +extern int bg_numItems; + +gitem_t *BG_FindItem( const char *pickupName ); +gitem_t *BG_FindItemForWeapon( weapon_t weapon ); +gitem_t *BG_FindItemForPowerup( powerup_t pw ); +gitem_t *BG_FindItemForHoldable( holdable_t pw ); +#define ITEM_INDEX(x) ((x)-bg_itemlist) + +qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const playerState_t *ps ); + + +// g_dmflags->integer flags +#define DF_NO_FALLING 8 +#define DF_FIXED_FOV 16 +#define DF_NO_FOOTSTEPS 32 + +// content masks +#define MASK_ALL (-1) +#define MASK_SOLID (CONTENTS_SOLID) +#define MASK_PLAYERSOLID (CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY) +#define MASK_DEADSOLID (CONTENTS_SOLID|CONTENTS_PLAYERCLIP) +#define MASK_WATER (CONTENTS_WATER|CONTENTS_LAVA|CONTENTS_SLIME) +#define MASK_OPAQUE (CONTENTS_SOLID|CONTENTS_SLIME|CONTENTS_LAVA) +#define MASK_SHOT (CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_CORPSE) + + +// +// entityState_t->eType +// +typedef enum { + ET_GENERAL, + ET_PLAYER, + ET_ITEM, + ET_MISSILE, + ET_MOVER, + ET_BEAM, + ET_PORTAL, + ET_SPEAKER, + ET_PUSH_TRIGGER, + ET_TELEPORT_TRIGGER, + ET_INVISIBLE, + ET_GRAPPLE, // grapple hooked on wall + ET_TEAM, + + ET_EVENTS // any of the EV_* events can be added freestanding + // by setting eType to ET_EVENTS + eventNum + // this avoids having to set eFlags and eventNum +} entityType_t; + + + +void BG_EvaluateTrajectory( const trajectory_t *tr, int atTime, vec3_t result ); +void BG_EvaluateTrajectoryDelta( const trajectory_t *tr, int atTime, vec3_t result ); + +void BG_AddPredictableEventToPlayerstate( int newEvent, int eventParm, playerState_t *ps ); + +void BG_TouchJumpPad( playerState_t *ps, entityState_t *jumppad ); + +void BG_PlayerStateToEntityState( playerState_t *ps, entityState_t *s, qboolean snap ); +void BG_PlayerStateToEntityStateExtraPolate( playerState_t *ps, entityState_t *s, int time, qboolean snap ); + +qboolean BG_PlayerTouchesItem( playerState_t *ps, entityState_t *item, int atTime ); + + +#define ARENAS_PER_TIER 4 +#define MAX_ARENAS 1024 +#define MAX_ARENAS_TEXT 8192 + +#define MAX_BOTS 1024 +#define MAX_BOTS_TEXT 8192 + + +// Kamikaze + +// 1st shockwave times +#define KAMI_SHOCKWAVE_STARTTIME 0 +#define KAMI_SHOCKWAVEFADE_STARTTIME 1500 +#define KAMI_SHOCKWAVE_ENDTIME 2000 +// explosion/implosion times +#define KAMI_EXPLODE_STARTTIME 250 +#define KAMI_IMPLODE_STARTTIME 2000 +#define KAMI_IMPLODE_ENDTIME 2250 +// 2nd shockwave times +#define KAMI_SHOCKWAVE2_STARTTIME 2000 +#define KAMI_SHOCKWAVE2FADE_STARTTIME 2500 +#define KAMI_SHOCKWAVE2_ENDTIME 3000 +// radius of the models without scaling +#define KAMI_SHOCKWAVEMODEL_RADIUS 88 +#define KAMI_BOOMSPHEREMODEL_RADIUS 72 +// maximum radius of the models during the effect +#define KAMI_SHOCKWAVE_MAXRADIUS 1320 +#define KAMI_BOOMSPHERE_MAXRADIUS 720 +#define KAMI_SHOCKWAVE2_MAXRADIUS 704 + diff --git a/reaction/engine/code/game/bg_slidemove.c b/reaction/engine/code/game/bg_slidemove.c new file mode 100644 index 00000000..14b4834f --- /dev/null +++ b/reaction/engine/code/game/bg_slidemove.c @@ -0,0 +1,325 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// bg_slidemove.c -- part of bg_pmove functionality + +#include "../qcommon/q_shared.h" +#include "bg_public.h" +#include "bg_local.h" + +/* + +input: origin, velocity, bounds, groundPlane, trace function + +output: origin, velocity, impacts, stairup boolean + +*/ + +/* +================== +PM_SlideMove + +Returns qtrue if the velocity was clipped in some way +================== +*/ +#define MAX_CLIP_PLANES 5 +qboolean PM_SlideMove( qboolean gravity ) { + int bumpcount, numbumps; + vec3_t dir; + float d; + int numplanes; + vec3_t planes[MAX_CLIP_PLANES]; + vec3_t primal_velocity; + vec3_t clipVelocity; + int i, j, k; + trace_t trace; + vec3_t end; + float time_left; + float into; + vec3_t endVelocity; + vec3_t endClipVelocity; + + numbumps = 4; + + VectorCopy (pm->ps->velocity, primal_velocity); + + if ( gravity ) { + VectorCopy( pm->ps->velocity, endVelocity ); + endVelocity[2] -= pm->ps->gravity * pml.frametime; + pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5; + primal_velocity[2] = endVelocity[2]; + if ( pml.groundPlane ) { + // slide along the ground plane + PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, + pm->ps->velocity, OVERCLIP ); + } + } + + time_left = pml.frametime; + + // never turn against the ground plane + if ( pml.groundPlane ) { + numplanes = 1; + VectorCopy( pml.groundTrace.plane.normal, planes[0] ); + } else { + numplanes = 0; + } + + // never turn against original velocity + VectorNormalize2( pm->ps->velocity, planes[numplanes] ); + numplanes++; + + for ( bumpcount=0 ; bumpcount < numbumps ; bumpcount++ ) { + + // calculate position we are trying to move to + VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end ); + + // see if we can make it there + pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask); + + if (trace.allsolid) { + // entity is completely trapped in another solid + pm->ps->velocity[2] = 0; // don't build up falling damage, but allow sideways acceleration + return qtrue; + } + + if (trace.fraction > 0) { + // actually covered some distance + VectorCopy (trace.endpos, pm->ps->origin); + } + + if (trace.fraction == 1) { + break; // moved the entire distance + } + + // save entity for contact + PM_AddTouchEnt( trace.entityNum ); + + time_left -= time_left * trace.fraction; + + if (numplanes >= MAX_CLIP_PLANES) { + // this shouldn't really happen + VectorClear( pm->ps->velocity ); + return qtrue; + } + + // + // if this is the same plane we hit before, nudge velocity + // out along it, which fixes some epsilon issues with + // non-axial planes + // + for ( i = 0 ; i < numplanes ; i++ ) { + if ( DotProduct( trace.plane.normal, planes[i] ) > 0.99 ) { + VectorAdd( trace.plane.normal, pm->ps->velocity, pm->ps->velocity ); + break; + } + } + if ( i < numplanes ) { + continue; + } + VectorCopy (trace.plane.normal, planes[numplanes]); + numplanes++; + + // + // modify velocity so it parallels all of the clip planes + // + + // find a plane that it enters + for ( i = 0 ; i < numplanes ; i++ ) { + into = DotProduct( pm->ps->velocity, planes[i] ); + if ( into >= 0.1 ) { + continue; // move doesn't interact with the plane + } + + // see how hard we are hitting things + if ( -into > pml.impactSpeed ) { + pml.impactSpeed = -into; + } + + // slide along the plane + PM_ClipVelocity (pm->ps->velocity, planes[i], clipVelocity, OVERCLIP ); + + // slide along the plane + PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP ); + + // see if there is a second plane that the new move enters + for ( j = 0 ; j < numplanes ; j++ ) { + if ( j == i ) { + continue; + } + if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) { + continue; // move doesn't interact with the plane + } + + // try clipping the move to the plane + PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP ); + PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP ); + + // see if it goes back into the first clip plane + if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) { + continue; + } + + // slide the original velocity along the crease + CrossProduct (planes[i], planes[j], dir); + VectorNormalize( dir ); + d = DotProduct( dir, pm->ps->velocity ); + VectorScale( dir, d, clipVelocity ); + + CrossProduct (planes[i], planes[j], dir); + VectorNormalize( dir ); + d = DotProduct( dir, endVelocity ); + VectorScale( dir, d, endClipVelocity ); + + // see if there is a third plane the the new move enters + for ( k = 0 ; k < numplanes ; k++ ) { + if ( k == i || k == j ) { + continue; + } + if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 ) { + continue; // move doesn't interact with the plane + } + + // stop dead at a tripple plane interaction + VectorClear( pm->ps->velocity ); + return qtrue; + } + } + + // if we have fixed all interactions, try another move + VectorCopy( clipVelocity, pm->ps->velocity ); + VectorCopy( endClipVelocity, endVelocity ); + break; + } + } + + if ( gravity ) { + VectorCopy( endVelocity, pm->ps->velocity ); + } + + // don't change velocity if in a timer (FIXME: is this correct?) + if ( pm->ps->pm_time ) { + VectorCopy( primal_velocity, pm->ps->velocity ); + } + + return ( bumpcount != 0 ); +} + +/* +================== +PM_StepSlideMove + +================== +*/ +void PM_StepSlideMove( qboolean gravity ) { + vec3_t start_o, start_v; + vec3_t down_o, down_v; + trace_t trace; +// float down_dist, up_dist; +// vec3_t delta, delta2; + vec3_t up, down; + float stepSize; + + VectorCopy (pm->ps->origin, start_o); + VectorCopy (pm->ps->velocity, start_v); + + if ( PM_SlideMove( gravity ) == 0 ) { + return; // we got exactly where we wanted to go first try + } + + VectorCopy(start_o, down); + down[2] -= STEPSIZE; + pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); + VectorSet(up, 0, 0, 1); + // never step up when you still have up velocity + if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 || + DotProduct(trace.plane.normal, up) < 0.7)) { + return; + } + + VectorCopy (pm->ps->origin, down_o); + VectorCopy (pm->ps->velocity, down_v); + + VectorCopy (start_o, up); + up[2] += STEPSIZE; + + // test the player position if they were a stepheight higher + pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); + if ( trace.allsolid ) { + if ( pm->debugLevel ) { + Com_Printf("%i:bend can't step\n", c_pmove); + } + return; // can't step up + } + + stepSize = trace.endpos[2] - start_o[2]; + // try slidemove from this position + VectorCopy (trace.endpos, pm->ps->origin); + VectorCopy (start_v, pm->ps->velocity); + + PM_SlideMove( gravity ); + + // push down the final amount + VectorCopy (pm->ps->origin, down); + down[2] -= stepSize; + pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); + if ( !trace.allsolid ) { + VectorCopy (trace.endpos, pm->ps->origin); + } + if ( trace.fraction < 1.0 ) { + PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); + } + +#if 0 + // if the down trace can trace back to the original position directly, don't step + pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, start_o, pm->ps->clientNum, pm->tracemask); + if ( trace.fraction == 1.0 ) { + // use the original move + VectorCopy (down_o, pm->ps->origin); + VectorCopy (down_v, pm->ps->velocity); + if ( pm->debugLevel ) { + Com_Printf("%i:bend\n", c_pmove); + } + } else +#endif + { + // use the step move + float delta; + + delta = pm->ps->origin[2] - start_o[2]; + if ( delta > 2 ) { + if ( delta < 7 ) { + PM_AddEvent( EV_STEP_4 ); + } else if ( delta < 11 ) { + PM_AddEvent( EV_STEP_8 ); + } else if ( delta < 15 ) { + PM_AddEvent( EV_STEP_12 ); + } else { + PM_AddEvent( EV_STEP_16 ); + } + } + if ( pm->debugLevel ) { + Com_Printf("%i:stepped\n", c_pmove); + } + } +} + diff --git a/reaction/engine/code/game/chars.h b/reaction/engine/code/game/chars.h new file mode 100644 index 00000000..4b570df1 --- /dev/null +++ b/reaction/engine/code/game/chars.h @@ -0,0 +1,134 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +//======================================================== +//======================================================== +//name +#define CHARACTERISTIC_NAME 0 //string +//gender of the bot +#define CHARACTERISTIC_GENDER 1 //string ("male", "female", "it") +//attack skill +// > 0.0 && < 0.2 = don't move +// > 0.3 && < 1.0 = aim at enemy during retreat +// > 0.0 && < 0.4 = only move forward/backward +// >= 0.4 && < 1.0 = circle strafing +// > 0.7 && < 1.0 = random strafe direction change +#define CHARACTERISTIC_ATTACK_SKILL 2 //float [0, 1] +//weapon weight file +#define CHARACTERISTIC_WEAPONWEIGHTS 3 //string +//view angle difference to angle change factor +#define CHARACTERISTIC_VIEW_FACTOR 4 //float <0, 1] +//maximum view angle change +#define CHARACTERISTIC_VIEW_MAXCHANGE 5 //float [1, 360] +//reaction time in seconds +#define CHARACTERISTIC_REACTIONTIME 6 //float [0, 5] +//accuracy when aiming +#define CHARACTERISTIC_AIM_ACCURACY 7 //float [0, 1] +//weapon specific aim accuracy +#define CHARACTERISTIC_AIM_ACCURACY_MACHINEGUN 8 //float [0, 1] +#define CHARACTERISTIC_AIM_ACCURACY_SHOTGUN 9 //float [0, 1] +#define CHARACTERISTIC_AIM_ACCURACY_ROCKETLAUNCHER 10 //float [0, 1] +#define CHARACTERISTIC_AIM_ACCURACY_GRENADELAUNCHER 11 //float [0, 1] +#define CHARACTERISTIC_AIM_ACCURACY_LIGHTNING 12 +#define CHARACTERISTIC_AIM_ACCURACY_PLASMAGUN 13 //float [0, 1] +#define CHARACTERISTIC_AIM_ACCURACY_RAILGUN 14 +#define CHARACTERISTIC_AIM_ACCURACY_BFG10K 15 //float [0, 1] +//skill when aiming +// > 0.0 && < 0.9 = aim is affected by enemy movement +// > 0.4 && <= 0.8 = enemy linear leading +// > 0.8 && <= 1.0 = enemy exact movement leading +// > 0.5 && <= 1.0 = prediction shots when enemy is not visible +// > 0.6 && <= 1.0 = splash damage by shooting nearby geometry +#define CHARACTERISTIC_AIM_SKILL 16 //float [0, 1] +//weapon specific aim skill +#define CHARACTERISTIC_AIM_SKILL_ROCKETLAUNCHER 17 //float [0, 1] +#define CHARACTERISTIC_AIM_SKILL_GRENADELAUNCHER 18 //float [0, 1] +#define CHARACTERISTIC_AIM_SKILL_PLASMAGUN 19 //float [0, 1] +#define CHARACTERISTIC_AIM_SKILL_BFG10K 20 //float [0, 1] +//======================================================== +//chat +//======================================================== +//file with chats +#define CHARACTERISTIC_CHAT_FILE 21 //string +//name of the chat character +#define CHARACTERISTIC_CHAT_NAME 22 //string +//characters per minute type speed +#define CHARACTERISTIC_CHAT_CPM 23 //integer [1, 4000] +//tendency to insult/praise +#define CHARACTERISTIC_CHAT_INSULT 24 //float [0, 1] +//tendency to chat misc +#define CHARACTERISTIC_CHAT_MISC 25 //float [0, 1] +//tendency to chat at start or end of level +#define CHARACTERISTIC_CHAT_STARTENDLEVEL 26 //float [0, 1] +//tendency to chat entering or exiting the game +#define CHARACTERISTIC_CHAT_ENTEREXITGAME 27 //float [0, 1] +//tendency to chat when killed someone +#define CHARACTERISTIC_CHAT_KILL 28 //float [0, 1] +//tendency to chat when died +#define CHARACTERISTIC_CHAT_DEATH 29 //float [0, 1] +//tendency to chat when enemy suicides +#define CHARACTERISTIC_CHAT_ENEMYSUICIDE 30 //float [0, 1] +//tendency to chat when hit while talking +#define CHARACTERISTIC_CHAT_HITTALKING 31 //float [0, 1] +//tendency to chat when bot was hit but didn't dye +#define CHARACTERISTIC_CHAT_HITNODEATH 32 //float [0, 1] +//tendency to chat when bot hit the enemy but enemy didn't dye +#define CHARACTERISTIC_CHAT_HITNOKILL 33 //float [0, 1] +//tendency to randomly chat +#define CHARACTERISTIC_CHAT_RANDOM 34 //float [0, 1] +//tendency to reply +#define CHARACTERISTIC_CHAT_REPLY 35 //float [0, 1] +//======================================================== +//movement +//======================================================== +//tendency to crouch +#define CHARACTERISTIC_CROUCHER 36 //float [0, 1] +//tendency to jump +#define CHARACTERISTIC_JUMPER 37 //float [0, 1] +//tendency to walk +#define CHARACTERISTIC_WALKER 48 //float [0, 1] +//tendency to jump using a weapon +#define CHARACTERISTIC_WEAPONJUMPING 38 //float [0, 1] +//tendency to use the grapple hook when available +#define CHARACTERISTIC_GRAPPLE_USER 39 //float [0, 1] //use this!! +//======================================================== +//goal +//======================================================== +//item weight file +#define CHARACTERISTIC_ITEMWEIGHTS 40 //string +//the aggression of the bot +#define CHARACTERISTIC_AGGRESSION 41 //float [0, 1] +//the self preservation of the bot (rockets near walls etc.) +#define CHARACTERISTIC_SELFPRESERVATION 42 //float [0, 1] +//how likely the bot is to take revenge +#define CHARACTERISTIC_VENGEFULNESS 43 //float [0, 1] //use this!! +//tendency to camp +#define CHARACTERISTIC_CAMPER 44 //float [0, 1] +//======================================================== +//======================================================== +//tendency to get easy frags +#define CHARACTERISTIC_EASY_FRAGGER 45 //float [0, 1] +//how alert the bot is (view distance) +#define CHARACTERISTIC_ALERTNESS 46 //float [0, 1] +//how much the bot fires it's weapon +#define CHARACTERISTIC_FIRETHROTTLE 47 //float [0, 1] + diff --git a/reaction/engine/code/game/g_active.c b/reaction/engine/code/game/g_active.c new file mode 100644 index 00000000..c830a5bd --- /dev/null +++ b/reaction/engine/code/game/g_active.c @@ -0,0 +1,1191 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +#include "g_local.h" + + +/* +=============== +G_DamageFeedback + +Called just before a snapshot is sent to the given player. +Totals up all damage and generates both the player_state_t +damage values to that client for pain blends and kicks, and +global pain sound events for all clients. +=============== +*/ +void P_DamageFeedback( gentity_t *player ) { + gclient_t *client; + float count; + vec3_t angles; + + client = player->client; + if ( client->ps.pm_type == PM_DEAD ) { + return; + } + + // total points of damage shot at the player this frame + count = client->damage_blood + client->damage_armor; + if ( count == 0 ) { + return; // didn't take any damage + } + + if ( count > 255 ) { + count = 255; + } + + // send the information to the client + + // world damage (falling, slime, etc) uses a special code + // to make the blend blob centered instead of positional + if ( client->damage_fromWorld ) { + client->ps.damagePitch = 255; + client->ps.damageYaw = 255; + + client->damage_fromWorld = qfalse; + } else { + vectoangles( client->damage_from, angles ); + client->ps.damagePitch = angles[PITCH]/360.0 * 256; + client->ps.damageYaw = angles[YAW]/360.0 * 256; + } + + // play an apropriate pain sound + if ( (level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) ) { + player->pain_debounce_time = level.time + 700; + G_AddEvent( player, EV_PAIN, player->health ); + client->ps.damageEvent++; + } + + + client->ps.damageCount = count; + + // + // clear totals + // + client->damage_blood = 0; + client->damage_armor = 0; + client->damage_knockback = 0; +} + + + +/* +============= +P_WorldEffects + +Check for lava / slime contents and drowning +============= +*/ +void P_WorldEffects( gentity_t *ent ) { + qboolean envirosuit; + int waterlevel; + + if ( ent->client->noclip ) { + ent->client->airOutTime = level.time + 12000; // don't need air + return; + } + + waterlevel = ent->waterlevel; + + envirosuit = ent->client->ps.powerups[PW_BATTLESUIT] > level.time; + + // + // check for drowning + // + if ( waterlevel == 3 ) { + // envirosuit give air + if ( envirosuit ) { + ent->client->airOutTime = level.time + 10000; + } + + // if out of air, start drowning + if ( ent->client->airOutTime < level.time) { + // drown! + ent->client->airOutTime += 1000; + if ( ent->health > 0 ) { + // take more damage the longer underwater + ent->damage += 2; + if (ent->damage > 15) + ent->damage = 15; + + // play a gurp sound instead of a normal pain sound + if (ent->health <= ent->damage) { + G_Sound(ent, CHAN_VOICE, G_SoundIndex("*drown.wav")); + } else if (rand()&1) { + G_Sound(ent, CHAN_VOICE, G_SoundIndex("sound/player/gurp1.wav")); + } else { + G_Sound(ent, CHAN_VOICE, G_SoundIndex("sound/player/gurp2.wav")); + } + + // don't play a normal pain sound + ent->pain_debounce_time = level.time + 200; + + G_Damage (ent, NULL, NULL, NULL, NULL, + ent->damage, DAMAGE_NO_ARMOR, MOD_WATER); + } + } + } else { + ent->client->airOutTime = level.time + 12000; + ent->damage = 2; + } + + // + // check for sizzle damage (move to pmove?) + // + if (waterlevel && + (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) { + if (ent->health > 0 + && ent->pain_debounce_time <= level.time ) { + + if ( envirosuit ) { + G_AddEvent( ent, EV_POWERUP_BATTLESUIT, 0 ); + } else { + if (ent->watertype & CONTENTS_LAVA) { + G_Damage (ent, NULL, NULL, NULL, NULL, + 30*waterlevel, 0, MOD_LAVA); + } + + if (ent->watertype & CONTENTS_SLIME) { + G_Damage (ent, NULL, NULL, NULL, NULL, + 10*waterlevel, 0, MOD_SLIME); + } + } + } + } +} + + + +/* +=============== +G_SetClientSound +=============== +*/ +void G_SetClientSound( gentity_t *ent ) { +#ifdef MISSIONPACK + if( ent->s.eFlags & EF_TICKING ) { + ent->client->ps.loopSound = G_SoundIndex( "sound/weapons/proxmine/wstbtick.wav"); + } + else +#endif + if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) { + ent->client->ps.loopSound = level.snd_fry; + } else { + ent->client->ps.loopSound = 0; + } +} + + + +//============================================================== + +/* +============== +ClientImpacts +============== +*/ +void ClientImpacts( gentity_t *ent, pmove_t *pm ) { + int i, j; + trace_t trace; + gentity_t *other; + + memset( &trace, 0, sizeof( trace ) ); + for (i=0 ; inumtouch ; i++) { + for (j=0 ; jtouchents[j] == pm->touchents[i] ) { + break; + } + } + if (j != i) { + continue; // duplicated + } + other = &g_entities[ pm->touchents[i] ]; + + if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) { + ent->touch( ent, other, &trace ); + } + + if ( !other->touch ) { + continue; + } + + other->touch( other, ent, &trace ); + } + +} + +/* +============ +G_TouchTriggers + +Find all trigger entities that ent's current position touches. +Spectators will only interact with teleporters. +============ +*/ +void G_TouchTriggers( gentity_t *ent ) { + int i, num; + int touch[MAX_GENTITIES]; + gentity_t *hit; + trace_t trace; + vec3_t mins, maxs; + static vec3_t range = { 40, 40, 52 }; + + if ( !ent->client ) { + return; + } + + // dead clients don't activate triggers! + if ( ent->client->ps.stats[STAT_HEALTH] <= 0 ) { + return; + } + + VectorSubtract( ent->client->ps.origin, range, mins ); + VectorAdd( ent->client->ps.origin, range, maxs ); + + num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); + + // can't use ent->absmin, because that has a one unit pad + VectorAdd( ent->client->ps.origin, ent->r.mins, mins ); + VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs ); + + for ( i=0 ; itouch && !ent->touch ) { + continue; + } + if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) { + continue; + } + + // ignore most entities if a spectator + if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { + if ( hit->s.eType != ET_TELEPORT_TRIGGER && + // this is ugly but adding a new ET_? type will + // most likely cause network incompatibilities + hit->touch != Touch_DoorTrigger) { + continue; + } + } + + // use seperate code for determining if an item is picked up + // so you don't have to actually contact its bounding box + if ( hit->s.eType == ET_ITEM ) { + if ( !BG_PlayerTouchesItem( &ent->client->ps, &hit->s, level.time ) ) { + continue; + } + } else { + if ( !trap_EntityContact( mins, maxs, hit ) ) { + continue; + } + } + + memset( &trace, 0, sizeof(trace) ); + + if ( hit->touch ) { + hit->touch (hit, ent, &trace); + } + + if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) { + ent->touch( ent, hit, &trace ); + } + } + + // if we didn't touch a jump pad this pmove frame + if ( ent->client->ps.jumppad_frame != ent->client->ps.pmove_framecount ) { + ent->client->ps.jumppad_frame = 0; + ent->client->ps.jumppad_ent = 0; + } +} + +/* +================= +SpectatorThink +================= +*/ +void SpectatorThink( gentity_t *ent, usercmd_t *ucmd ) { + pmove_t pm; + gclient_t *client; + + client = ent->client; + + if ( client->sess.spectatorState != SPECTATOR_FOLLOW ) { + client->ps.pm_type = PM_SPECTATOR; + client->ps.speed = 400; // faster than normal + + // set up for pmove + memset (&pm, 0, sizeof(pm)); + pm.ps = &client->ps; + pm.cmd = *ucmd; + pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; // spectators can fly through bodies + pm.trace = trap_Trace; + pm.pointcontents = trap_PointContents; + + // perform a pmove + Pmove (&pm); + // save results of pmove + VectorCopy( client->ps.origin, ent->s.origin ); + + G_TouchTriggers( ent ); + trap_UnlinkEntity( ent ); + } + + client->oldbuttons = client->buttons; + client->buttons = ucmd->buttons; + + // attack button cycles through spectators + if ( ( client->buttons & BUTTON_ATTACK ) && ! ( client->oldbuttons & BUTTON_ATTACK ) ) { + Cmd_FollowCycle_f( ent, 1 ); + } +} + + + +/* +================= +ClientInactivityTimer + +Returns qfalse if the client is dropped +================= +*/ +qboolean ClientInactivityTimer( gclient_t *client ) { + if ( ! g_inactivity.integer ) { + // give everyone some time, so if the operator sets g_inactivity during + // gameplay, everyone isn't kicked + client->inactivityTime = level.time + 60 * 1000; + client->inactivityWarning = qfalse; + } else if ( client->pers.cmd.forwardmove || + client->pers.cmd.rightmove || + client->pers.cmd.upmove || + (client->pers.cmd.buttons & BUTTON_ATTACK) ) { + client->inactivityTime = level.time + g_inactivity.integer * 1000; + client->inactivityWarning = qfalse; + } else if ( !client->pers.localClient ) { + if ( level.time > client->inactivityTime ) { + trap_DropClient( client - level.clients, "Dropped due to inactivity" ); + return qfalse; + } + if ( level.time > client->inactivityTime - 10000 && !client->inactivityWarning ) { + client->inactivityWarning = qtrue; + trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" ); + } + } + return qtrue; +} + +/* +================== +ClientTimerActions + +Actions that happen once a second +================== +*/ +void ClientTimerActions( gentity_t *ent, int msec ) { + gclient_t *client; +#ifdef MISSIONPACK + int maxHealth; +#endif + + client = ent->client; + client->timeResidual += msec; + + while ( client->timeResidual >= 1000 ) { + client->timeResidual -= 1000; + + // regenerate +#ifdef MISSIONPACK + if( bg_itemlist[client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { + maxHealth = client->ps.stats[STAT_MAX_HEALTH] / 2; + } + else if ( client->ps.powerups[PW_REGEN] ) { + maxHealth = client->ps.stats[STAT_MAX_HEALTH]; + } + else { + maxHealth = 0; + } + if( maxHealth ) { + if ( ent->health < maxHealth ) { + ent->health += 15; + if ( ent->health > maxHealth * 1.1 ) { + ent->health = maxHealth * 1.1; + } + G_AddEvent( ent, EV_POWERUP_REGEN, 0 ); + } else if ( ent->health < maxHealth * 2) { + ent->health += 5; + if ( ent->health > maxHealth * 2 ) { + ent->health = maxHealth * 2; + } + G_AddEvent( ent, EV_POWERUP_REGEN, 0 ); + } +#else + if ( client->ps.powerups[PW_REGEN] ) { + if ( ent->health < client->ps.stats[STAT_MAX_HEALTH]) { + ent->health += 15; + if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] * 1.1 ) { + ent->health = client->ps.stats[STAT_MAX_HEALTH] * 1.1; + } + G_AddEvent( ent, EV_POWERUP_REGEN, 0 ); + } else if ( ent->health < client->ps.stats[STAT_MAX_HEALTH] * 2) { + ent->health += 5; + if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] * 2 ) { + ent->health = client->ps.stats[STAT_MAX_HEALTH] * 2; + } + G_AddEvent( ent, EV_POWERUP_REGEN, 0 ); + } +#endif + } else { + // count down health when over max + if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] ) { + ent->health--; + } + } + + // count down armor when over max + if ( client->ps.stats[STAT_ARMOR] > client->ps.stats[STAT_MAX_HEALTH] ) { + client->ps.stats[STAT_ARMOR]--; + } + } +#ifdef MISSIONPACK + if( bg_itemlist[client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) { + int w, max, inc, t, i; + int weapList[]={WP_MACHINEGUN,WP_SHOTGUN,WP_GRENADE_LAUNCHER,WP_ROCKET_LAUNCHER,WP_LIGHTNING,WP_RAILGUN,WP_PLASMAGUN,WP_BFG,WP_NAILGUN,WP_PROX_LAUNCHER,WP_CHAINGUN}; + int weapCount = sizeof(weapList) / sizeof(int); + // + for (i = 0; i < weapCount; i++) { + w = weapList[i]; + + switch(w) { + case WP_MACHINEGUN: max = 50; inc = 4; t = 1000; break; + case WP_SHOTGUN: max = 10; inc = 1; t = 1500; break; + case WP_GRENADE_LAUNCHER: max = 10; inc = 1; t = 2000; break; + case WP_ROCKET_LAUNCHER: max = 10; inc = 1; t = 1750; break; + case WP_LIGHTNING: max = 50; inc = 5; t = 1500; break; + case WP_RAILGUN: max = 10; inc = 1; t = 1750; break; + case WP_PLASMAGUN: max = 50; inc = 5; t = 1500; break; + case WP_BFG: max = 10; inc = 1; t = 4000; break; + case WP_NAILGUN: max = 10; inc = 1; t = 1250; break; + case WP_PROX_LAUNCHER: max = 5; inc = 1; t = 2000; break; + case WP_CHAINGUN: max = 100; inc = 5; t = 1000; break; + default: max = 0; inc = 0; t = 1000; break; + } + client->ammoTimes[w] += msec; + if ( client->ps.ammo[w] >= max ) { + client->ammoTimes[w] = 0; + } + if ( client->ammoTimes[w] >= t ) { + while ( client->ammoTimes[w] >= t ) + client->ammoTimes[w] -= t; + client->ps.ammo[w] += inc; + if ( client->ps.ammo[w] > max ) { + client->ps.ammo[w] = max; + } + } + } + } +#endif +} + +/* +==================== +ClientIntermissionThink +==================== +*/ +void ClientIntermissionThink( gclient_t *client ) { + client->ps.eFlags &= ~EF_TALK; + client->ps.eFlags &= ~EF_FIRING; + + // the level will exit when everyone wants to or after timeouts + + // swap and latch button actions + client->oldbuttons = client->buttons; + client->buttons = client->pers.cmd.buttons; + if ( client->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) & ( client->oldbuttons ^ client->buttons ) ) { + // this used to be an ^1 but once a player says ready, it should stick + client->readyToExit = 1; + } +} + + +/* +================ +ClientEvents + +Events will be passed on to the clients for presentation, +but any server game effects are handled here +================ +*/ +void ClientEvents( gentity_t *ent, int oldEventSequence ) { + int i, j; + int event; + gclient_t *client; + int damage; + vec3_t dir; + vec3_t origin, angles; +// qboolean fired; + gitem_t *item; + gentity_t *drop; + + client = ent->client; + + if ( oldEventSequence < client->ps.eventSequence - MAX_PS_EVENTS ) { + oldEventSequence = client->ps.eventSequence - MAX_PS_EVENTS; + } + for ( i = oldEventSequence ; i < client->ps.eventSequence ; i++ ) { + event = client->ps.events[ i & (MAX_PS_EVENTS-1) ]; + + switch ( event ) { + case EV_FALL_MEDIUM: + case EV_FALL_FAR: + if ( ent->s.eType != ET_PLAYER ) { + break; // not in the player model + } + if ( g_dmflags.integer & DF_NO_FALLING ) { + break; + } + if ( event == EV_FALL_FAR ) { + damage = 10; + } else { + damage = 5; + } + VectorSet (dir, 0, 0, 1); + ent->pain_debounce_time = level.time + 200; // no normal pain sound + G_Damage (ent, NULL, NULL, NULL, NULL, damage, 0, MOD_FALLING); + break; + + case EV_FIRE_WEAPON: + FireWeapon( ent ); + break; + + case EV_USE_ITEM1: // teleporter + // drop flags in CTF + item = NULL; + j = 0; + + if ( ent->client->ps.powerups[ PW_REDFLAG ] ) { + item = BG_FindItemForPowerup( PW_REDFLAG ); + j = PW_REDFLAG; + } else if ( ent->client->ps.powerups[ PW_BLUEFLAG ] ) { + item = BG_FindItemForPowerup( PW_BLUEFLAG ); + j = PW_BLUEFLAG; + } else if ( ent->client->ps.powerups[ PW_NEUTRALFLAG ] ) { + item = BG_FindItemForPowerup( PW_NEUTRALFLAG ); + j = PW_NEUTRALFLAG; + } + + if ( item ) { + drop = Drop_Item( ent, item, 0 ); + // decide how many seconds it has left + drop->count = ( ent->client->ps.powerups[ j ] - level.time ) / 1000; + if ( drop->count < 1 ) { + drop->count = 1; + } + + ent->client->ps.powerups[ j ] = 0; + } + +#ifdef MISSIONPACK + if ( g_gametype.integer == GT_HARVESTER ) { + if ( ent->client->ps.generic1 > 0 ) { + if ( ent->client->sess.sessionTeam == TEAM_RED ) { + item = BG_FindItem( "Blue Cube" ); + } else { + item = BG_FindItem( "Red Cube" ); + } + if ( item ) { + for ( j = 0; j < ent->client->ps.generic1; j++ ) { + drop = Drop_Item( ent, item, 0 ); + if ( ent->client->sess.sessionTeam == TEAM_RED ) { + drop->spawnflags = TEAM_BLUE; + } else { + drop->spawnflags = TEAM_RED; + } + } + } + ent->client->ps.generic1 = 0; + } + } +#endif + SelectSpawnPoint( ent->client->ps.origin, origin, angles ); + TeleportPlayer( ent, origin, angles ); + break; + + case EV_USE_ITEM2: // medkit + ent->health = ent->client->ps.stats[STAT_MAX_HEALTH] + 25; + + break; + +#ifdef MISSIONPACK + case EV_USE_ITEM3: // kamikaze + // make sure the invulnerability is off + ent->client->invulnerabilityTime = 0; + // start the kamikze + G_StartKamikaze( ent ); + break; + + case EV_USE_ITEM4: // portal + if( ent->client->portalID ) { + DropPortalSource( ent ); + } + else { + DropPortalDestination( ent ); + } + break; + case EV_USE_ITEM5: // invulnerability + ent->client->invulnerabilityTime = level.time + 10000; + break; +#endif + + default: + break; + } + } + +} + +#ifdef MISSIONPACK +/* +============== +StuckInOtherClient +============== +*/ +static int StuckInOtherClient(gentity_t *ent) { + int i; + gentity_t *ent2; + + ent2 = &g_entities[0]; + for ( i = 0; i < MAX_CLIENTS; i++, ent2++ ) { + if ( ent2 == ent ) { + continue; + } + if ( !ent2->inuse ) { + continue; + } + if ( !ent2->client ) { + continue; + } + if ( ent2->health <= 0 ) { + continue; + } + // + if (ent2->r.absmin[0] > ent->r.absmax[0]) + continue; + if (ent2->r.absmin[1] > ent->r.absmax[1]) + continue; + if (ent2->r.absmin[2] > ent->r.absmax[2]) + continue; + if (ent2->r.absmax[0] < ent->r.absmin[0]) + continue; + if (ent2->r.absmax[1] < ent->r.absmin[1]) + continue; + if (ent2->r.absmax[2] < ent->r.absmin[2]) + continue; + return qtrue; + } + return qfalse; +} +#endif + +void BotTestSolid(vec3_t origin); + +/* +============== +SendPendingPredictableEvents +============== +*/ +void SendPendingPredictableEvents( playerState_t *ps ) { + gentity_t *t; + int event, seq; + int extEvent, number; + + // if there are still events pending + if ( ps->entityEventSequence < ps->eventSequence ) { + // create a temporary entity for this event which is sent to everyone + // except the client who generated the event + seq = ps->entityEventSequence & (MAX_PS_EVENTS-1); + event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 ); + // set external event to zero before calling BG_PlayerStateToEntityState + extEvent = ps->externalEvent; + ps->externalEvent = 0; + // create temporary entity for event + t = G_TempEntity( ps->origin, event ); + number = t->s.number; + BG_PlayerStateToEntityState( ps, &t->s, qtrue ); + t->s.number = number; + t->s.eType = ET_EVENTS + event; + t->s.eFlags |= EF_PLAYER_EVENT; + t->s.otherEntityNum = ps->clientNum; + // send to everyone except the client who generated the event + t->r.svFlags |= SVF_NOTSINGLECLIENT; + t->r.singleClient = ps->clientNum; + // set back external event + ps->externalEvent = extEvent; + } +} + +/* +============== +ClientThink + +This will be called once for each client frame, which will +usually be a couple times for each server frame on fast clients. + +If "g_synchronousClients 1" is set, this will be called exactly +once for each server frame, which makes for smooth demo recording. +============== +*/ +void ClientThink_real( gentity_t *ent ) { + gclient_t *client; + pmove_t pm; + int oldEventSequence; + int msec; + usercmd_t *ucmd; + + client = ent->client; + + // don't think if the client is not yet connected (and thus not yet spawned in) + if (client->pers.connected != CON_CONNECTED) { + return; + } + // mark the time, so the connection sprite can be removed + ucmd = &ent->client->pers.cmd; + + // sanity check the command time to prevent speedup cheating + if ( ucmd->serverTime > level.time + 200 ) { + ucmd->serverTime = level.time + 200; +// G_Printf("serverTime <<<<<\n" ); + } + if ( ucmd->serverTime < level.time - 1000 ) { + ucmd->serverTime = level.time - 1000; +// G_Printf("serverTime >>>>>\n" ); + } + + msec = ucmd->serverTime - client->ps.commandTime; + // following others may result in bad times, but we still want + // to check for follow toggles + if ( msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW ) { + return; + } + if ( msec > 200 ) { + msec = 200; + } + + if ( pmove_msec.integer < 8 ) { + trap_Cvar_Set("pmove_msec", "8"); + } + else if (pmove_msec.integer > 33) { + trap_Cvar_Set("pmove_msec", "33"); + } + + if ( pmove_fixed.integer || client->pers.pmoveFixed ) { + ucmd->serverTime = ((ucmd->serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer; + //if (ucmd->serverTime - client->ps.commandTime <= 0) + // return; + } + + // + // check for exiting intermission + // + if ( level.intermissiontime ) { + ClientIntermissionThink( client ); + return; + } + + // spectators don't do much + if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { + if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) { + return; + } + SpectatorThink( ent, ucmd ); + return; + } + + // check for inactivity timer, but never drop the local client of a non-dedicated server + if ( !ClientInactivityTimer( client ) ) { + return; + } + + // clear the rewards if time + if ( level.time > client->rewardTime ) { + client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); + } + + if ( client->noclip ) { + client->ps.pm_type = PM_NOCLIP; + } else if ( client->ps.stats[STAT_HEALTH] <= 0 ) { + client->ps.pm_type = PM_DEAD; + } else { + client->ps.pm_type = PM_NORMAL; + } + + client->ps.gravity = g_gravity.value; + + // set speed + client->ps.speed = g_speed.value; + +#ifdef MISSIONPACK + if( bg_itemlist[client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) { + client->ps.speed *= 1.5; + } + else +#endif + if ( client->ps.powerups[PW_HASTE] ) { + client->ps.speed *= 1.3; + } + + // Let go of the hook if we aren't firing + if ( client->ps.weapon == WP_GRAPPLING_HOOK && + client->hook && !( ucmd->buttons & BUTTON_ATTACK ) ) { + Weapon_HookFree(client->hook); + } + + // set up for pmove + oldEventSequence = client->ps.eventSequence; + + memset (&pm, 0, sizeof(pm)); + + // check for the hit-scan gauntlet, don't let the action + // go through as an attack unless it actually hits something + if ( client->ps.weapon == WP_GAUNTLET && !( ucmd->buttons & BUTTON_TALK ) && + ( ucmd->buttons & BUTTON_ATTACK ) && client->ps.weaponTime <= 0 ) { + pm.gauntletHit = CheckGauntletAttack( ent ); + } + + if ( ent->flags & FL_FORCE_GESTURE ) { + ent->flags &= ~FL_FORCE_GESTURE; + ent->client->pers.cmd.buttons |= BUTTON_GESTURE; + } + +#ifdef MISSIONPACK + // check for invulnerability expansion before doing the Pmove + if (client->ps.powerups[PW_INVULNERABILITY] ) { + if ( !(client->ps.pm_flags & PMF_INVULEXPAND) ) { + vec3_t mins = { -42, -42, -42 }; + vec3_t maxs = { 42, 42, 42 }; + vec3_t oldmins, oldmaxs; + + VectorCopy (ent->r.mins, oldmins); + VectorCopy (ent->r.maxs, oldmaxs); + // expand + VectorCopy (mins, ent->r.mins); + VectorCopy (maxs, ent->r.maxs); + trap_LinkEntity(ent); + // check if this would get anyone stuck in this player + if ( !StuckInOtherClient(ent) ) { + // set flag so the expanded size will be set in PM_CheckDuck + client->ps.pm_flags |= PMF_INVULEXPAND; + } + // set back + VectorCopy (oldmins, ent->r.mins); + VectorCopy (oldmaxs, ent->r.maxs); + trap_LinkEntity(ent); + } + } +#endif + + pm.ps = &client->ps; + pm.cmd = *ucmd; + if ( pm.ps->pm_type == PM_DEAD ) { + pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; + } + else if ( ent->r.svFlags & SVF_BOT ) { + pm.tracemask = MASK_PLAYERSOLID | CONTENTS_BOTCLIP; + } + else { + pm.tracemask = MASK_PLAYERSOLID; + } + pm.trace = trap_Trace; + pm.pointcontents = trap_PointContents; + pm.debugLevel = g_debugMove.integer; + pm.noFootsteps = ( g_dmflags.integer & DF_NO_FOOTSTEPS ) > 0; + + pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed; + pm.pmove_msec = pmove_msec.integer; + + VectorCopy( client->ps.origin, client->oldOrigin ); + +#ifdef MISSIONPACK + if (level.intermissionQueued != 0 && g_singlePlayer.integer) { + if ( level.time - level.intermissionQueued >= 1000 ) { + pm.cmd.buttons = 0; + pm.cmd.forwardmove = 0; + pm.cmd.rightmove = 0; + pm.cmd.upmove = 0; + if ( level.time - level.intermissionQueued >= 2000 && level.time - level.intermissionQueued <= 2500 ) { + trap_SendConsoleCommand( EXEC_APPEND, "centerview\n"); + } + ent->client->ps.pm_type = PM_SPINTERMISSION; + } + } + Pmove (&pm); +#else + Pmove (&pm); +#endif + + // save results of pmove + if ( ent->client->ps.eventSequence != oldEventSequence ) { + ent->eventTime = level.time; + } + if (g_smoothClients.integer) { + BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue ); + } + else { + BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue ); + } + SendPendingPredictableEvents( &ent->client->ps ); + + if ( !( ent->client->ps.eFlags & EF_FIRING ) ) { + client->fireHeld = qfalse; // for grapple + } + + // use the snapped origin for linking so it matches client predicted versions + VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin ); + + VectorCopy (pm.mins, ent->r.mins); + VectorCopy (pm.maxs, ent->r.maxs); + + ent->waterlevel = pm.waterlevel; + ent->watertype = pm.watertype; + + // execute client events + ClientEvents( ent, oldEventSequence ); + + // link entity now, after any personal teleporters have been used + trap_LinkEntity (ent); + if ( !ent->client->noclip ) { + G_TouchTriggers( ent ); + } + + // NOTE: now copy the exact origin over otherwise clients can be snapped into solid + VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); + + //test for solid areas in the AAS file + BotTestAAS(ent->r.currentOrigin); + + // touch other objects + ClientImpacts( ent, &pm ); + + // save results of triggers and client events + if (ent->client->ps.eventSequence != oldEventSequence) { + ent->eventTime = level.time; + } + + // swap and latch button actions + client->oldbuttons = client->buttons; + client->buttons = ucmd->buttons; + client->latched_buttons |= client->buttons & ~client->oldbuttons; + + // check for respawning + if ( client->ps.stats[STAT_HEALTH] <= 0 ) { + // wait for the attack button to be pressed + if ( level.time > client->respawnTime ) { + // forcerespawn is to prevent users from waiting out powerups + if ( g_forcerespawn.integer > 0 && + ( level.time - client->respawnTime ) > g_forcerespawn.integer * 1000 ) { + respawn( ent ); + return; + } + + // pressing attack or use is the normal respawn method + if ( ucmd->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) ) { + respawn( ent ); + } + } + return; + } + + // perform once-a-second actions + ClientTimerActions( ent, msec ); +} + +/* +================== +ClientThink + +A new command has arrived from the client +================== +*/ +void ClientThink( int clientNum ) { + gentity_t *ent; + + ent = g_entities + clientNum; + trap_GetUsercmd( clientNum, &ent->client->pers.cmd ); + + // mark the time we got info, so we can display the + // phone jack if they don't get any for a while + ent->client->lastCmdTime = level.time; + + if ( !(ent->r.svFlags & SVF_BOT) && !g_synchronousClients.integer ) { + ClientThink_real( ent ); + } +} + + +void G_RunClient( gentity_t *ent ) { + if ( !(ent->r.svFlags & SVF_BOT) && !g_synchronousClients.integer ) { + return; + } + ent->client->pers.cmd.serverTime = level.time; + ClientThink_real( ent ); +} + + +/* +================== +SpectatorClientEndFrame + +================== +*/ +void SpectatorClientEndFrame( gentity_t *ent ) { + gclient_t *cl; + + // if we are doing a chase cam or a remote view, grab the latest info + if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) { + int clientNum, flags; + + clientNum = ent->client->sess.spectatorClient; + + // team follow1 and team follow2 go to whatever clients are playing + if ( clientNum == -1 ) { + clientNum = level.follow1; + } else if ( clientNum == -2 ) { + clientNum = level.follow2; + } + if ( clientNum >= 0 ) { + cl = &level.clients[ clientNum ]; + if ( cl->pers.connected == CON_CONNECTED && cl->sess.sessionTeam != TEAM_SPECTATOR ) { + flags = (cl->ps.eFlags & ~(EF_VOTED | EF_TEAMVOTED)) | (ent->client->ps.eFlags & (EF_VOTED | EF_TEAMVOTED)); + ent->client->ps = cl->ps; + ent->client->ps.pm_flags |= PMF_FOLLOW; + ent->client->ps.eFlags = flags; + return; + } else { + // drop them to free spectators unless they are dedicated camera followers + if ( ent->client->sess.spectatorClient >= 0 ) { + ent->client->sess.spectatorState = SPECTATOR_FREE; + ClientBegin( ent->client - level.clients ); + } + } + } + } + + if ( ent->client->sess.spectatorState == SPECTATOR_SCOREBOARD ) { + ent->client->ps.pm_flags |= PMF_SCOREBOARD; + } else { + ent->client->ps.pm_flags &= ~PMF_SCOREBOARD; + } +} + +/* +============== +ClientEndFrame + +Called at the end of each server frame for each connected client +A fast client will have multiple ClientThink for each ClientEdFrame, +while a slow client may have multiple ClientEndFrame between ClientThink. +============== +*/ +void ClientEndFrame( gentity_t *ent ) { + int i; + clientPersistant_t *pers; + + if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { + SpectatorClientEndFrame( ent ); + return; + } + + pers = &ent->client->pers; + + // turn off any expired powerups + for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { + if ( ent->client->ps.powerups[ i ] < level.time ) { + ent->client->ps.powerups[ i ] = 0; + } + } + +#ifdef MISSIONPACK + // set powerup for player animation + if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { + ent->client->ps.powerups[PW_GUARD] = level.time; + } + if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) { + ent->client->ps.powerups[PW_SCOUT] = level.time; + } + if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_DOUBLER ) { + ent->client->ps.powerups[PW_DOUBLER] = level.time; + } + if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) { + ent->client->ps.powerups[PW_AMMOREGEN] = level.time; + } + if ( ent->client->invulnerabilityTime > level.time ) { + ent->client->ps.powerups[PW_INVULNERABILITY] = level.time; + } +#endif + + // save network bandwidth +#if 0 + if ( !g_synchronousClients->integer && ent->client->ps.pm_type == PM_NORMAL ) { + // FIXME: this must change eventually for non-sync demo recording + VectorClear( ent->client->ps.viewangles ); + } +#endif + + // + // If the end of unit layout is displayed, don't give + // the player any normal movement attributes + // + if ( level.intermissiontime ) { + return; + } + + // burn from lava, etc + P_WorldEffects (ent); + + // apply all the damage taken this frame + P_DamageFeedback (ent); + + // add the EF_CONNECTION flag if we haven't gotten commands recently + if ( level.time - ent->client->lastCmdTime > 1000 ) { + ent->s.eFlags |= EF_CONNECTION; + } else { + ent->s.eFlags &= ~EF_CONNECTION; + } + + ent->client->ps.stats[STAT_HEALTH] = ent->health; // FIXME: get rid of ent->health... + + G_SetClientSound (ent); + + // set the latest infor + if (g_smoothClients.integer) { + BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue ); + } + else { + BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue ); + } + SendPendingPredictableEvents( &ent->client->ps ); + + // set the bit for the reachability area the client is currently in +// i = trap_AAS_PointReachabilityAreaIndex( ent->client->ps.origin ); +// ent->client->areabits[i >> 3] |= 1 << (i & 7); +} + + diff --git a/reaction/engine/code/game/g_arenas.c b/reaction/engine/code/game/g_arenas.c new file mode 100644 index 00000000..da96c479 --- /dev/null +++ b/reaction/engine/code/game/g_arenas.c @@ -0,0 +1,376 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// +// g_arenas.c +// + +#include "g_local.h" + + +gentity_t *podium1; +gentity_t *podium2; +gentity_t *podium3; + + +/* +================== +UpdateTournamentInfo +================== +*/ +void UpdateTournamentInfo( void ) { + int i; + gentity_t *player; + int playerClientNum; + int n, accuracy, perfect, msglen; + int buflen; +#ifdef MISSIONPACK + int score1, score2; + qboolean won; +#endif + char buf[32]; + char msg[MAX_STRING_CHARS]; + + // find the real player + player = NULL; + for (i = 0; i < level.maxclients; i++ ) { + player = &g_entities[i]; + if ( !player->inuse ) { + continue; + } + if ( !( player->r.svFlags & SVF_BOT ) ) { + break; + } + } + // this should never happen! + if ( !player || i == level.maxclients ) { + return; + } + playerClientNum = i; + + CalculateRanks(); + + if ( level.clients[playerClientNum].sess.sessionTeam == TEAM_SPECTATOR ) { +#ifdef MISSIONPACK + Com_sprintf( msg, sizeof(msg), "postgame %i %i 0 0 0 0 0 0 0 0 0 0 0", level.numNonSpectatorClients, playerClientNum ); +#else + Com_sprintf( msg, sizeof(msg), "postgame %i %i 0 0 0 0 0 0", level.numNonSpectatorClients, playerClientNum ); +#endif + } + else { + if( player->client->accuracy_shots ) { + accuracy = player->client->accuracy_hits * 100 / player->client->accuracy_shots; + } + else { + accuracy = 0; + } +#ifdef MISSIONPACK + won = qfalse; + if (g_gametype.integer >= GT_CTF) { + score1 = level.teamScores[TEAM_RED]; + score2 = level.teamScores[TEAM_BLUE]; + if (level.clients[playerClientNum].sess.sessionTeam == TEAM_RED) { + won = (level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE]); + } else { + won = (level.teamScores[TEAM_BLUE] > level.teamScores[TEAM_RED]); + } + } else { + if (&level.clients[playerClientNum] == &level.clients[ level.sortedClients[0] ]) { + won = qtrue; + score1 = level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE]; + score2 = level.clients[ level.sortedClients[1] ].ps.persistant[PERS_SCORE]; + } else { + score2 = level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE]; + score1 = level.clients[ level.sortedClients[1] ].ps.persistant[PERS_SCORE]; + } + } + if (won && player->client->ps.persistant[PERS_KILLED] == 0) { + perfect = 1; + } else { + perfect = 0; + } + Com_sprintf( msg, sizeof(msg), "postgame %i %i %i %i %i %i %i %i %i %i %i %i %i %i", level.numNonSpectatorClients, playerClientNum, accuracy, + player->client->ps.persistant[PERS_IMPRESSIVE_COUNT], player->client->ps.persistant[PERS_EXCELLENT_COUNT],player->client->ps.persistant[PERS_DEFEND_COUNT], + player->client->ps.persistant[PERS_ASSIST_COUNT], player->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], player->client->ps.persistant[PERS_SCORE], + perfect, score1, score2, level.time, player->client->ps.persistant[PERS_CAPTURES] ); + +#else + perfect = ( level.clients[playerClientNum].ps.persistant[PERS_RANK] == 0 && player->client->ps.persistant[PERS_KILLED] == 0 ) ? 1 : 0; + Com_sprintf( msg, sizeof(msg), "postgame %i %i %i %i %i %i %i %i", level.numNonSpectatorClients, playerClientNum, accuracy, + player->client->ps.persistant[PERS_IMPRESSIVE_COUNT], player->client->ps.persistant[PERS_EXCELLENT_COUNT], + player->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], player->client->ps.persistant[PERS_SCORE], + perfect ); +#endif + } + + msglen = strlen( msg ); + for( i = 0; i < level.numNonSpectatorClients; i++ ) { + n = level.sortedClients[i]; + Com_sprintf( buf, sizeof(buf), " %i %i %i", n, level.clients[n].ps.persistant[PERS_RANK], level.clients[n].ps.persistant[PERS_SCORE] ); + buflen = strlen( buf ); + if( msglen + buflen + 1 >= sizeof(msg) ) { + break; + } + strcat( msg, buf ); + } + trap_SendConsoleCommand( EXEC_APPEND, msg ); +} + + +static gentity_t *SpawnModelOnVictoryPad( gentity_t *pad, vec3_t offset, gentity_t *ent, int place ) { + gentity_t *body; + vec3_t vec; + vec3_t f, r, u; + + body = G_Spawn(); + if ( !body ) { + G_Printf( S_COLOR_RED "ERROR: out of gentities\n" ); + return NULL; + } + + body->classname = ent->client->pers.netname; + body->client = ent->client; + body->s = ent->s; + body->s.eType = ET_PLAYER; // could be ET_INVISIBLE + body->s.eFlags = 0; // clear EF_TALK, etc + body->s.powerups = 0; // clear powerups + body->s.loopSound = 0; // clear lava burning + body->s.number = body - g_entities; + body->timestamp = level.time; + body->physicsObject = qtrue; + body->physicsBounce = 0; // don't bounce + body->s.event = 0; + body->s.pos.trType = TR_STATIONARY; + body->s.groundEntityNum = ENTITYNUM_WORLD; + body->s.legsAnim = LEGS_IDLE; + body->s.torsoAnim = TORSO_STAND; + if( body->s.weapon == WP_NONE ) { + body->s.weapon = WP_MACHINEGUN; + } + if( body->s.weapon == WP_GAUNTLET) { + body->s.torsoAnim = TORSO_STAND2; + } + body->s.event = 0; + body->r.svFlags = ent->r.svFlags; + VectorCopy (ent->r.mins, body->r.mins); + VectorCopy (ent->r.maxs, body->r.maxs); + VectorCopy (ent->r.absmin, body->r.absmin); + VectorCopy (ent->r.absmax, body->r.absmax); + body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP; + body->r.contents = CONTENTS_BODY; + body->r.ownerNum = ent->r.ownerNum; + body->takedamage = qfalse; + + VectorSubtract( level.intermission_origin, pad->r.currentOrigin, vec ); + vectoangles( vec, body->s.apos.trBase ); + body->s.apos.trBase[PITCH] = 0; + body->s.apos.trBase[ROLL] = 0; + + AngleVectors( body->s.apos.trBase, f, r, u ); + VectorMA( pad->r.currentOrigin, offset[0], f, vec ); + VectorMA( vec, offset[1], r, vec ); + VectorMA( vec, offset[2], u, vec ); + + G_SetOrigin( body, vec ); + + trap_LinkEntity (body); + + body->count = place; + + return body; +} + + +static void CelebrateStop( gentity_t *player ) { + int anim; + + if( player->s.weapon == WP_GAUNTLET) { + anim = TORSO_STAND2; + } + else { + anim = TORSO_STAND; + } + player->s.torsoAnim = ( ( player->s.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; +} + + +#define TIMER_GESTURE (34*66+50) +static void CelebrateStart( gentity_t *player ) { + player->s.torsoAnim = ( ( player->s.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | TORSO_GESTURE; + player->nextthink = level.time + TIMER_GESTURE; + player->think = CelebrateStop; + + /* + player->client->ps.events[player->client->ps.eventSequence & (MAX_PS_EVENTS-1)] = EV_TAUNT; + player->client->ps.eventParms[player->client->ps.eventSequence & (MAX_PS_EVENTS-1)] = 0; + player->client->ps.eventSequence++; + */ + G_AddEvent(player, EV_TAUNT, 0); +} + + +static vec3_t offsetFirst = {0, 0, 74}; +static vec3_t offsetSecond = {-10, 60, 54}; +static vec3_t offsetThird = {-19, -60, 45}; + +static void PodiumPlacementThink( gentity_t *podium ) { + vec3_t vec; + vec3_t origin; + vec3_t f, r, u; + + podium->nextthink = level.time + 100; + + AngleVectors( level.intermission_angle, vec, NULL, NULL ); + VectorMA( level.intermission_origin, trap_Cvar_VariableIntegerValue( "g_podiumDist" ), vec, origin ); + origin[2] -= trap_Cvar_VariableIntegerValue( "g_podiumDrop" ); + G_SetOrigin( podium, origin ); + + if( podium1 ) { + VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec ); + vectoangles( vec, podium1->s.apos.trBase ); + podium1->s.apos.trBase[PITCH] = 0; + podium1->s.apos.trBase[ROLL] = 0; + + AngleVectors( podium1->s.apos.trBase, f, r, u ); + VectorMA( podium->r.currentOrigin, offsetFirst[0], f, vec ); + VectorMA( vec, offsetFirst[1], r, vec ); + VectorMA( vec, offsetFirst[2], u, vec ); + + G_SetOrigin( podium1, vec ); + } + + if( podium2 ) { + VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec ); + vectoangles( vec, podium2->s.apos.trBase ); + podium2->s.apos.trBase[PITCH] = 0; + podium2->s.apos.trBase[ROLL] = 0; + + AngleVectors( podium2->s.apos.trBase, f, r, u ); + VectorMA( podium->r.currentOrigin, offsetSecond[0], f, vec ); + VectorMA( vec, offsetSecond[1], r, vec ); + VectorMA( vec, offsetSecond[2], u, vec ); + + G_SetOrigin( podium2, vec ); + } + + if( podium3 ) { + VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec ); + vectoangles( vec, podium3->s.apos.trBase ); + podium3->s.apos.trBase[PITCH] = 0; + podium3->s.apos.trBase[ROLL] = 0; + + AngleVectors( podium3->s.apos.trBase, f, r, u ); + VectorMA( podium->r.currentOrigin, offsetThird[0], f, vec ); + VectorMA( vec, offsetThird[1], r, vec ); + VectorMA( vec, offsetThird[2], u, vec ); + + G_SetOrigin( podium3, vec ); + } +} + + +static gentity_t *SpawnPodium( void ) { + gentity_t *podium; + vec3_t vec; + vec3_t origin; + + podium = G_Spawn(); + if ( !podium ) { + return NULL; + } + + podium->classname = "podium"; + podium->s.eType = ET_GENERAL; + podium->s.number = podium - g_entities; + podium->clipmask = CONTENTS_SOLID; + podium->r.contents = CONTENTS_SOLID; + podium->s.modelindex = G_ModelIndex( SP_PODIUM_MODEL ); + + AngleVectors( level.intermission_angle, vec, NULL, NULL ); + VectorMA( level.intermission_origin, trap_Cvar_VariableIntegerValue( "g_podiumDist" ), vec, origin ); + origin[2] -= trap_Cvar_VariableIntegerValue( "g_podiumDrop" ); + G_SetOrigin( podium, origin ); + + VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec ); + podium->s.apos.trBase[YAW] = vectoyaw( vec ); + trap_LinkEntity (podium); + + podium->think = PodiumPlacementThink; + podium->nextthink = level.time + 100; + return podium; +} + + +/* +================== +SpawnModelsOnVictoryPads +================== +*/ +void SpawnModelsOnVictoryPads( void ) { + gentity_t *player; + gentity_t *podium; + + podium1 = NULL; + podium2 = NULL; + podium3 = NULL; + + podium = SpawnPodium(); + + player = SpawnModelOnVictoryPad( podium, offsetFirst, &g_entities[level.sortedClients[0]], + level.clients[ level.sortedClients[0] ].ps.persistant[PERS_RANK] &~ RANK_TIED_FLAG ); + if ( player ) { + player->nextthink = level.time + 2000; + player->think = CelebrateStart; + podium1 = player; + } + + player = SpawnModelOnVictoryPad( podium, offsetSecond, &g_entities[level.sortedClients[1]], + level.clients[ level.sortedClients[1] ].ps.persistant[PERS_RANK] &~ RANK_TIED_FLAG ); + if ( player ) { + podium2 = player; + } + + if ( level.numNonSpectatorClients > 2 ) { + player = SpawnModelOnVictoryPad( podium, offsetThird, &g_entities[level.sortedClients[2]], + level.clients[ level.sortedClients[2] ].ps.persistant[PERS_RANK] &~ RANK_TIED_FLAG ); + if ( player ) { + podium3 = player; + } + } +} + + +/* +=============== +Svcmd_AbortPodium_f +=============== +*/ +void Svcmd_AbortPodium_f( void ) { + if( g_gametype.integer != GT_SINGLE_PLAYER ) { + return; + } + + if( podium1 ) { + podium1->nextthink = level.time; + podium1->think = CelebrateStop; + } +} diff --git a/reaction/engine/code/game/g_bot.c b/reaction/engine/code/game/g_bot.c new file mode 100644 index 00000000..4c9cf8ce --- /dev/null +++ b/reaction/engine/code/game/g_bot.c @@ -0,0 +1,1013 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// g_bot.c + +#include "g_local.h" + + +static int g_numBots; +static char *g_botInfos[MAX_BOTS]; + + +int g_numArenas; +static char *g_arenaInfos[MAX_ARENAS]; + + +#define BOT_BEGIN_DELAY_BASE 2000 +#define BOT_BEGIN_DELAY_INCREMENT 1500 + +#define BOT_SPAWN_QUEUE_DEPTH 16 + +typedef struct { + int clientNum; + int spawnTime; +} botSpawnQueue_t; + +static botSpawnQueue_t botSpawnQueue[BOT_SPAWN_QUEUE_DEPTH]; + +vmCvar_t bot_minplayers; + +extern gentity_t *podium1; +extern gentity_t *podium2; +extern gentity_t *podium3; + +float trap_Cvar_VariableValue( const char *var_name ) { + char buf[128]; + + trap_Cvar_VariableStringBuffer(var_name, buf, sizeof(buf)); + return atof(buf); +} + + + +/* +=============== +G_ParseInfos +=============== +*/ +int G_ParseInfos( char *buf, int max, char *infos[] ) { + char *token; + int count; + char key[MAX_TOKEN_CHARS]; + char info[MAX_INFO_STRING]; + + count = 0; + + while ( 1 ) { + token = COM_Parse( &buf ); + if ( !token[0] ) { + break; + } + if ( strcmp( token, "{" ) ) { + Com_Printf( "Missing { in info file\n" ); + break; + } + + if ( count == max ) { + Com_Printf( "Max infos exceeded\n" ); + break; + } + + info[0] = '\0'; + while ( 1 ) { + token = COM_ParseExt( &buf, qtrue ); + if ( !token[0] ) { + Com_Printf( "Unexpected end of info file\n" ); + break; + } + if ( !strcmp( token, "}" ) ) { + break; + } + Q_strncpyz( key, token, sizeof( key ) ); + + token = COM_ParseExt( &buf, qfalse ); + if ( !token[0] ) { + strcpy( token, "" ); + } + Info_SetValueForKey( info, key, token ); + } + //NOTE: extra space for arena number + infos[count] = G_Alloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1); + if (infos[count]) { + strcpy(infos[count], info); + count++; + } + } + return count; +} + +/* +=============== +G_LoadArenasFromFile +=============== +*/ +static void G_LoadArenasFromFile( char *filename ) { + int len; + fileHandle_t f; + char buf[MAX_ARENAS_TEXT]; + + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + if ( !f ) { + trap_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) ); + return; + } + if ( len >= MAX_ARENAS_TEXT ) { + trap_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_ARENAS_TEXT ) ); + trap_FS_FCloseFile( f ); + return; + } + + trap_FS_Read( buf, len, f ); + buf[len] = 0; + trap_FS_FCloseFile( f ); + + g_numArenas += G_ParseInfos( buf, MAX_ARENAS - g_numArenas, &g_arenaInfos[g_numArenas] ); +} + +/* +=============== +G_LoadArenas +=============== +*/ +static void G_LoadArenas( void ) { + int numdirs; + vmCvar_t arenasFile; + char filename[128]; + char dirlist[1024]; + char* dirptr; + int i, n; + int dirlen; + + g_numArenas = 0; + + trap_Cvar_Register( &arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM ); + if( *arenasFile.string ) { + G_LoadArenasFromFile(arenasFile.string); + } + else { + G_LoadArenasFromFile("scripts/arenas.txt"); + } + + // get all arenas from .arena files + numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 1024 ); + dirptr = dirlist; + for (i = 0; i < numdirs; i++, dirptr += dirlen+1) { + dirlen = strlen(dirptr); + strcpy(filename, "scripts/"); + strcat(filename, dirptr); + G_LoadArenasFromFile(filename); + } + trap_Printf( va( "%i arenas parsed\n", g_numArenas ) ); + + for( n = 0; n < g_numArenas; n++ ) { + Info_SetValueForKey( g_arenaInfos[n], "num", va( "%i", n ) ); + } +} + + +/* +=============== +G_GetArenaInfoByNumber +=============== +*/ +const char *G_GetArenaInfoByMap( const char *map ) { + int n; + + for( n = 0; n < g_numArenas; n++ ) { + if( Q_stricmp( Info_ValueForKey( g_arenaInfos[n], "map" ), map ) == 0 ) { + return g_arenaInfos[n]; + } + } + + return NULL; +} + + +/* +================= +PlayerIntroSound +================= +*/ +static void PlayerIntroSound( const char *modelAndSkin ) { + char model[MAX_QPATH]; + char *skin; + + Q_strncpyz( model, modelAndSkin, sizeof(model) ); + skin = Q_strrchr( model, '/' ); + if ( skin ) { + *skin++ = '\0'; + } + else { + skin = model; + } + + if( Q_stricmp( skin, "default" ) == 0 ) { + skin = model; + } + + trap_SendConsoleCommand( EXEC_APPEND, va( "play sound/player/announce/%s.wav\n", skin ) ); +} + +/* +=============== +G_AddRandomBot +=============== +*/ +void G_AddRandomBot( int team ) { + int i, n, num; + float skill; + char *value, netname[36], *teamstr; + gclient_t *cl; + + num = 0; + for ( n = 0; n < g_numBots ; n++ ) { + value = Info_ValueForKey( g_botInfos[n], "name" ); + // + for ( i=0 ; i< g_maxclients.integer ; i++ ) { + cl = level.clients + i; + if ( cl->pers.connected != CON_CONNECTED ) { + continue; + } + if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) { + continue; + } + if ( team >= 0 && cl->sess.sessionTeam != team ) { + continue; + } + if ( !Q_stricmp( value, cl->pers.netname ) ) { + break; + } + } + if (i >= g_maxclients.integer) { + num++; + } + } + num = random() * num; + for ( n = 0; n < g_numBots ; n++ ) { + value = Info_ValueForKey( g_botInfos[n], "name" ); + // + for ( i=0 ; i< g_maxclients.integer ; i++ ) { + cl = level.clients + i; + if ( cl->pers.connected != CON_CONNECTED ) { + continue; + } + if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) { + continue; + } + if ( team >= 0 && cl->sess.sessionTeam != team ) { + continue; + } + if ( !Q_stricmp( value, cl->pers.netname ) ) { + break; + } + } + if (i >= g_maxclients.integer) { + num--; + if (num <= 0) { + skill = trap_Cvar_VariableValue( "g_spSkill" ); + if (team == TEAM_RED) teamstr = "red"; + else if (team == TEAM_BLUE) teamstr = "blue"; + else teamstr = ""; + strncpy(netname, value, sizeof(netname)-1); + netname[sizeof(netname)-1] = '\0'; + Q_CleanStr(netname); + trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %f %s %i\n", netname, skill, teamstr, 0) ); + return; + } + } + } +} + +/* +=============== +G_RemoveRandomBot +=============== +*/ +int G_RemoveRandomBot( int team ) { + int i; + gclient_t *cl; + + for ( i=0 ; i< g_maxclients.integer ; i++ ) { + cl = level.clients + i; + if ( cl->pers.connected != CON_CONNECTED ) { + continue; + } + if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) { + continue; + } + if ( team >= 0 && cl->sess.sessionTeam != team ) { + continue; + } + trap_SendConsoleCommand( EXEC_INSERT, va("clientkick %d\n", cl->ps.clientNum) ); + return qtrue; + } + return qfalse; +} + +/* +=============== +G_CountHumanPlayers +=============== +*/ +int G_CountHumanPlayers( int team ) { + int i, num; + gclient_t *cl; + + num = 0; + for ( i=0 ; i< g_maxclients.integer ; i++ ) { + cl = level.clients + i; + if ( cl->pers.connected != CON_CONNECTED ) { + continue; + } + if ( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) { + continue; + } + if ( team >= 0 && cl->sess.sessionTeam != team ) { + continue; + } + num++; + } + return num; +} + +/* +=============== +G_CountBotPlayers +=============== +*/ +int G_CountBotPlayers( int team ) { + int i, n, num; + gclient_t *cl; + + num = 0; + for ( i=0 ; i< g_maxclients.integer ; i++ ) { + cl = level.clients + i; + if ( cl->pers.connected != CON_CONNECTED ) { + continue; + } + if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) { + continue; + } + if ( team >= 0 && cl->sess.sessionTeam != team ) { + continue; + } + num++; + } + for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) { + if( !botSpawnQueue[n].spawnTime ) { + continue; + } + if ( botSpawnQueue[n].spawnTime > level.time ) { + continue; + } + num++; + } + return num; +} + +/* +=============== +G_CheckMinimumPlayers +=============== +*/ +void G_CheckMinimumPlayers( void ) { + int minplayers; + int humanplayers, botplayers; + static int checkminimumplayers_time; + + if (level.intermissiontime) return; + //only check once each 10 seconds + if (checkminimumplayers_time > level.time - 10000) { + return; + } + checkminimumplayers_time = level.time; + trap_Cvar_Update(&bot_minplayers); + minplayers = bot_minplayers.integer; + if (minplayers <= 0) return; + + if (g_gametype.integer >= GT_TEAM) { + if (minplayers >= g_maxclients.integer / 2) { + minplayers = (g_maxclients.integer / 2) -1; + } + + humanplayers = G_CountHumanPlayers( TEAM_RED ); + botplayers = G_CountBotPlayers( TEAM_RED ); + // + if (humanplayers + botplayers < minplayers) { + G_AddRandomBot( TEAM_RED ); + } else if (humanplayers + botplayers > minplayers && botplayers) { + G_RemoveRandomBot( TEAM_RED ); + } + // + humanplayers = G_CountHumanPlayers( TEAM_BLUE ); + botplayers = G_CountBotPlayers( TEAM_BLUE ); + // + if (humanplayers + botplayers < minplayers) { + G_AddRandomBot( TEAM_BLUE ); + } else if (humanplayers + botplayers > minplayers && botplayers) { + G_RemoveRandomBot( TEAM_BLUE ); + } + } + else if (g_gametype.integer == GT_TOURNAMENT ) { + if (minplayers >= g_maxclients.integer) { + minplayers = g_maxclients.integer-1; + } + humanplayers = G_CountHumanPlayers( -1 ); + botplayers = G_CountBotPlayers( -1 ); + // + if (humanplayers + botplayers < minplayers) { + G_AddRandomBot( TEAM_FREE ); + } else if (humanplayers + botplayers > minplayers && botplayers) { + // try to remove spectators first + if (!G_RemoveRandomBot( TEAM_SPECTATOR )) { + // just remove the bot that is playing + G_RemoveRandomBot( -1 ); + } + } + } + else if (g_gametype.integer == GT_FFA) { + if (minplayers >= g_maxclients.integer) { + minplayers = g_maxclients.integer-1; + } + humanplayers = G_CountHumanPlayers( TEAM_FREE ); + botplayers = G_CountBotPlayers( TEAM_FREE ); + // + if (humanplayers + botplayers < minplayers) { + G_AddRandomBot( TEAM_FREE ); + } else if (humanplayers + botplayers > minplayers && botplayers) { + G_RemoveRandomBot( TEAM_FREE ); + } + } +} + +/* +=============== +G_CheckBotSpawn +=============== +*/ +void G_CheckBotSpawn( void ) { + int n; + char userinfo[MAX_INFO_VALUE]; + + G_CheckMinimumPlayers(); + + for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) { + if( !botSpawnQueue[n].spawnTime ) { + continue; + } + if ( botSpawnQueue[n].spawnTime > level.time ) { + continue; + } + ClientBegin( botSpawnQueue[n].clientNum ); + botSpawnQueue[n].spawnTime = 0; + + if( g_gametype.integer == GT_SINGLE_PLAYER ) { + trap_GetUserinfo( botSpawnQueue[n].clientNum, userinfo, sizeof(userinfo) ); + PlayerIntroSound( Info_ValueForKey (userinfo, "model") ); + } + } +} + + +/* +=============== +AddBotToSpawnQueue +=============== +*/ +static void AddBotToSpawnQueue( int clientNum, int delay ) { + int n; + + for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) { + if( !botSpawnQueue[n].spawnTime ) { + botSpawnQueue[n].spawnTime = level.time + delay; + botSpawnQueue[n].clientNum = clientNum; + return; + } + } + + G_Printf( S_COLOR_YELLOW "Unable to delay spawn\n" ); + ClientBegin( clientNum ); +} + + +/* +=============== +G_RemoveQueuedBotBegin + +Called on client disconnect to make sure the delayed spawn +doesn't happen on a freed index +=============== +*/ +void G_RemoveQueuedBotBegin( int clientNum ) { + int n; + + for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) { + if( botSpawnQueue[n].clientNum == clientNum ) { + botSpawnQueue[n].spawnTime = 0; + return; + } + } +} + + +/* +=============== +G_BotConnect +=============== +*/ +qboolean G_BotConnect( int clientNum, qboolean restart ) { + bot_settings_t settings; + char userinfo[MAX_INFO_STRING]; + + trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) ); + + Q_strncpyz( settings.characterfile, Info_ValueForKey( userinfo, "characterfile" ), sizeof(settings.characterfile) ); + settings.skill = atof( Info_ValueForKey( userinfo, "skill" ) ); + Q_strncpyz( settings.team, Info_ValueForKey( userinfo, "team" ), sizeof(settings.team) ); + + if (!BotAISetupClient( clientNum, &settings, restart )) { + trap_DropClient( clientNum, "BotAISetupClient failed" ); + return qfalse; + } + + return qtrue; +} + + +/* +=============== +G_AddBot +=============== +*/ +static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { + int clientNum; + char *botinfo; + gentity_t *bot; + char *key; + char *s; + char *botname; + char *model; + char *headmodel; + char userinfo[MAX_INFO_STRING]; + + // get the botinfo from bots.txt + botinfo = G_GetBotInfoByName( name ); + if ( !botinfo ) { + G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); + return; + } + + // create the bot's userinfo + userinfo[0] = '\0'; + + botname = Info_ValueForKey( botinfo, "funname" ); + if( !botname[0] ) { + botname = Info_ValueForKey( botinfo, "name" ); + } + // check for an alternative name + if (altname && altname[0]) { + botname = altname; + } + Info_SetValueForKey( userinfo, "name", botname ); + Info_SetValueForKey( userinfo, "rate", "25000" ); + Info_SetValueForKey( userinfo, "snaps", "20" ); + Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); + + if ( skill >= 1 && skill < 2 ) { + Info_SetValueForKey( userinfo, "handicap", "50" ); + } + else if ( skill >= 2 && skill < 3 ) { + Info_SetValueForKey( userinfo, "handicap", "70" ); + } + else if ( skill >= 3 && skill < 4 ) { + Info_SetValueForKey( userinfo, "handicap", "90" ); + } + + key = "model"; + model = Info_ValueForKey( botinfo, key ); + if ( !*model ) { + model = "visor/default"; + } + Info_SetValueForKey( userinfo, key, model ); + key = "team_model"; + Info_SetValueForKey( userinfo, key, model ); + + key = "headmodel"; + headmodel = Info_ValueForKey( botinfo, key ); + if ( !*headmodel ) { + headmodel = model; + } + Info_SetValueForKey( userinfo, key, headmodel ); + key = "team_headmodel"; + Info_SetValueForKey( userinfo, key, headmodel ); + + key = "gender"; + s = Info_ValueForKey( botinfo, key ); + if ( !*s ) { + s = "male"; + } + Info_SetValueForKey( userinfo, "sex", s ); + + key = "color1"; + s = Info_ValueForKey( botinfo, key ); + if ( !*s ) { + s = "4"; + } + Info_SetValueForKey( userinfo, key, s ); + + key = "color2"; + s = Info_ValueForKey( botinfo, key ); + if ( !*s ) { + s = "5"; + } + Info_SetValueForKey( userinfo, key, s ); + + s = Info_ValueForKey(botinfo, "aifile"); + if (!*s ) { + trap_Printf( S_COLOR_RED "Error: bot has no aifile specified\n" ); + return; + } + + // have the server allocate a client slot + clientNum = trap_BotAllocateClient(); + if ( clientNum == -1 ) { + G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); + G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" ); + return; + } + + // initialize the bot settings + if( !team || !*team ) { + if( g_gametype.integer >= GT_TEAM ) { + if( PickTeam(clientNum) == TEAM_RED) { + team = "red"; + } + else { + team = "blue"; + } + } + else { + team = "red"; + } + } + Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); + Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); + Info_SetValueForKey( userinfo, "team", team ); + + bot = &g_entities[ clientNum ]; + bot->r.svFlags |= SVF_BOT; + bot->inuse = qtrue; + + // register the userinfo + trap_SetUserinfo( clientNum, userinfo ); + + // have it connect to the game as a normal client + if ( ClientConnect( clientNum, qtrue, qtrue ) ) { + return; + } + + if( delay == 0 ) { + ClientBegin( clientNum ); + return; + } + + AddBotToSpawnQueue( clientNum, delay ); +} + + +/* +=============== +Svcmd_AddBot_f +=============== +*/ +void Svcmd_AddBot_f( void ) { + float skill; + int delay; + char name[MAX_TOKEN_CHARS]; + char altname[MAX_TOKEN_CHARS]; + char string[MAX_TOKEN_CHARS]; + char team[MAX_TOKEN_CHARS]; + + // are bots enabled? + if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) { + return; + } + + // name + trap_Argv( 1, name, sizeof( name ) ); + if ( !name[0] ) { + trap_Printf( "Usage: Addbot [skill 1-5] [team] [msec delay] [altname]\n" ); + return; + } + + // skill + trap_Argv( 2, string, sizeof( string ) ); + if ( !string[0] ) { + skill = 4; + } + else { + skill = atof( string ); + } + + // team + trap_Argv( 3, team, sizeof( team ) ); + + // delay + trap_Argv( 4, string, sizeof( string ) ); + if ( !string[0] ) { + delay = 0; + } + else { + delay = atoi( string ); + } + + // alternative name + trap_Argv( 5, altname, sizeof( altname ) ); + + G_AddBot( name, skill, team, delay, altname ); + + // if this was issued during gameplay and we are playing locally, + // go ahead and load the bot's media immediately + if ( level.time - level.startTime > 1000 && + trap_Cvar_VariableIntegerValue( "cl_running" ) ) { + trap_SendServerCommand( -1, "loaddefered\n" ); // FIXME: spelled wrong, but not changing for demo + } +} + +/* +=============== +Svcmd_BotList_f +=============== +*/ +void Svcmd_BotList_f( void ) { + int i; + char name[MAX_TOKEN_CHARS]; + char funname[MAX_TOKEN_CHARS]; + char model[MAX_TOKEN_CHARS]; + char aifile[MAX_TOKEN_CHARS]; + + trap_Printf("^1name model aifile funname\n"); + for (i = 0; i < g_numBots; i++) { + strcpy(name, Info_ValueForKey( g_botInfos[i], "name" )); + if ( !*name ) { + strcpy(name, "UnnamedPlayer"); + } + strcpy(funname, Info_ValueForKey( g_botInfos[i], "funname" )); + if ( !*funname ) { + strcpy(funname, ""); + } + strcpy(model, Info_ValueForKey( g_botInfos[i], "model" )); + if ( !*model ) { + strcpy(model, "visor/default"); + } + strcpy(aifile, Info_ValueForKey( g_botInfos[i], "aifile")); + if (!*aifile ) { + strcpy(aifile, "bots/default_c.c"); + } + trap_Printf(va("%-16s %-16s %-20s %-20s\n", name, model, aifile, funname)); + } +} + + +/* +=============== +G_SpawnBots +=============== +*/ +static void G_SpawnBots( char *botList, int baseDelay ) { + char *bot; + char *p; + float skill; + int delay; + char bots[MAX_INFO_VALUE]; + + podium1 = NULL; + podium2 = NULL; + podium3 = NULL; + + skill = trap_Cvar_VariableValue( "g_spSkill" ); + if( skill < 1 ) { + trap_Cvar_Set( "g_spSkill", "1" ); + skill = 1; + } + else if ( skill > 5 ) { + trap_Cvar_Set( "g_spSkill", "5" ); + skill = 5; + } + + Q_strncpyz( bots, botList, sizeof(bots) ); + p = &bots[0]; + delay = baseDelay; + while( *p ) { + //skip spaces + while( *p && *p == ' ' ) { + p++; + } + if( !p ) { + break; + } + + // mark start of bot name + bot = p; + + // skip until space of null + while( *p && *p != ' ' ) { + p++; + } + if( *p ) { + *p++ = 0; + } + + // we must add the bot this way, calling G_AddBot directly at this stage + // does "Bad Things" + trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %f free %i\n", bot, skill, delay) ); + + delay += BOT_BEGIN_DELAY_INCREMENT; + } +} + + +/* +=============== +G_LoadBotsFromFile +=============== +*/ +static void G_LoadBotsFromFile( char *filename ) { + int len; + fileHandle_t f; + char buf[MAX_BOTS_TEXT]; + + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + if ( !f ) { + trap_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) ); + return; + } + if ( len >= MAX_BOTS_TEXT ) { + trap_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_BOTS_TEXT ) ); + trap_FS_FCloseFile( f ); + return; + } + + trap_FS_Read( buf, len, f ); + buf[len] = 0; + trap_FS_FCloseFile( f ); + + g_numBots += G_ParseInfos( buf, MAX_BOTS - g_numBots, &g_botInfos[g_numBots] ); +} + +/* +=============== +G_LoadBots +=============== +*/ +static void G_LoadBots( void ) { + vmCvar_t botsFile; + int numdirs; + char filename[128]; + char dirlist[1024]; + char* dirptr; + int i; + int dirlen; + + if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) { + return; + } + + g_numBots = 0; + + trap_Cvar_Register( &botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM ); + if( *botsFile.string ) { + G_LoadBotsFromFile(botsFile.string); + } + else { + G_LoadBotsFromFile("scripts/bots.txt"); + } + + // get all bots from .bot files + numdirs = trap_FS_GetFileList("scripts", ".bot", dirlist, 1024 ); + dirptr = dirlist; + for (i = 0; i < numdirs; i++, dirptr += dirlen+1) { + dirlen = strlen(dirptr); + strcpy(filename, "scripts/"); + strcat(filename, dirptr); + G_LoadBotsFromFile(filename); + } + trap_Printf( va( "%i bots parsed\n", g_numBots ) ); +} + + + +/* +=============== +G_GetBotInfoByNumber +=============== +*/ +char *G_GetBotInfoByNumber( int num ) { + if( num < 0 || num >= g_numBots ) { + trap_Printf( va( S_COLOR_RED "Invalid bot number: %i\n", num ) ); + return NULL; + } + return g_botInfos[num]; +} + + +/* +=============== +G_GetBotInfoByName +=============== +*/ +char *G_GetBotInfoByName( const char *name ) { + int n; + char *value; + + for ( n = 0; n < g_numBots ; n++ ) { + value = Info_ValueForKey( g_botInfos[n], "name" ); + if ( !Q_stricmp( value, name ) ) { + return g_botInfos[n]; + } + } + + return NULL; +} + +/* +=============== +G_InitBots +=============== +*/ +void G_InitBots( qboolean restart ) { + int fragLimit; + int timeLimit; + const char *arenainfo; + char *strValue; + int basedelay; + char map[MAX_QPATH]; + char serverinfo[MAX_INFO_STRING]; + + G_LoadBots(); + G_LoadArenas(); + + trap_Cvar_Register( &bot_minplayers, "bot_minplayers", "0", CVAR_SERVERINFO ); + + if( g_gametype.integer == GT_SINGLE_PLAYER ) { + trap_GetServerinfo( serverinfo, sizeof(serverinfo) ); + Q_strncpyz( map, Info_ValueForKey( serverinfo, "mapname" ), sizeof(map) ); + arenainfo = G_GetArenaInfoByMap( map ); + if ( !arenainfo ) { + return; + } + + strValue = Info_ValueForKey( arenainfo, "fraglimit" ); + fragLimit = atoi( strValue ); + if ( fragLimit ) { + trap_Cvar_Set( "fraglimit", strValue ); + } + else { + trap_Cvar_Set( "fraglimit", "0" ); + } + + strValue = Info_ValueForKey( arenainfo, "timelimit" ); + timeLimit = atoi( strValue ); + if ( timeLimit ) { + trap_Cvar_Set( "timelimit", strValue ); + } + else { + trap_Cvar_Set( "timelimit", "0" ); + } + + if ( !fragLimit && !timeLimit ) { + trap_Cvar_Set( "fraglimit", "10" ); + trap_Cvar_Set( "timelimit", "0" ); + } + + basedelay = BOT_BEGIN_DELAY_BASE; + strValue = Info_ValueForKey( arenainfo, "special" ); + if( Q_stricmp( strValue, "training" ) == 0 ) { + basedelay += 10000; + } + + if( !restart ) { + G_SpawnBots( Info_ValueForKey( arenainfo, "bots" ), basedelay ); + } + } +} diff --git a/reaction/engine/code/game/g_client.c b/reaction/engine/code/game/g_client.c new file mode 100644 index 00000000..575d2e04 --- /dev/null +++ b/reaction/engine/code/game/g_client.c @@ -0,0 +1,1358 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +#include "g_local.h" + +// g_client.c -- client functions that don't happen every frame + +static vec3_t playerMins = {-15, -15, -24}; +static vec3_t playerMaxs = {15, 15, 32}; + +/*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) initial +potential spawning position for deathmatch games. +The first time a player enters the game, they will be at an 'initial' spot. +Targets will be fired when someone spawns in on them. +"nobots" will prevent bots from using this spot. +"nohumans" will prevent non-bots from using this spot. +*/ +void SP_info_player_deathmatch( gentity_t *ent ) { + int i; + + G_SpawnInt( "nobots", "0", &i); + if ( i ) { + ent->flags |= FL_NO_BOTS; + } + G_SpawnInt( "nohumans", "0", &i ); + if ( i ) { + ent->flags |= FL_NO_HUMANS; + } +} + +/*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) +equivelant to info_player_deathmatch +*/ +void SP_info_player_start(gentity_t *ent) { + ent->classname = "info_player_deathmatch"; + SP_info_player_deathmatch( ent ); +} + +/*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32) +The intermission will be viewed from this point. Target an info_notnull for the view direction. +*/ +void SP_info_player_intermission( gentity_t *ent ) { + +} + + + +/* +======================================================================= + + SelectSpawnPoint + +======================================================================= +*/ + +/* +================ +SpotWouldTelefrag + +================ +*/ +qboolean SpotWouldTelefrag( gentity_t *spot ) { + int i, num; + int touch[MAX_GENTITIES]; + gentity_t *hit; + vec3_t mins, maxs; + + VectorAdd( spot->s.origin, playerMins, mins ); + VectorAdd( spot->s.origin, playerMaxs, maxs ); + num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); + + for (i=0 ; iclient && hit->client->ps.stats[STAT_HEALTH] > 0 ) { + if ( hit->client) { + return qtrue; + } + + } + + return qfalse; +} + +/* +================ +SelectNearestDeathmatchSpawnPoint + +Find the spot that we DON'T want to use +================ +*/ +#define MAX_SPAWN_POINTS 128 +gentity_t *SelectNearestDeathmatchSpawnPoint( vec3_t from ) { + gentity_t *spot; + vec3_t delta; + float dist, nearestDist; + gentity_t *nearestSpot; + + nearestDist = 999999; + nearestSpot = NULL; + spot = NULL; + + while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { + + VectorSubtract( spot->s.origin, from, delta ); + dist = VectorLength( delta ); + if ( dist < nearestDist ) { + nearestDist = dist; + nearestSpot = spot; + } + } + + return nearestSpot; +} + + +/* +================ +SelectRandomDeathmatchSpawnPoint + +go to a random point that doesn't telefrag +================ +*/ +#define MAX_SPAWN_POINTS 128 +gentity_t *SelectRandomDeathmatchSpawnPoint( void ) { + gentity_t *spot; + int count; + int selection; + gentity_t *spots[MAX_SPAWN_POINTS]; + + count = 0; + spot = NULL; + + while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { + if ( SpotWouldTelefrag( spot ) ) { + continue; + } + spots[ count ] = spot; + count++; + } + + if ( !count ) { // no spots that won't telefrag + return G_Find( NULL, FOFS(classname), "info_player_deathmatch"); + } + + selection = rand() % count; + return spots[ selection ]; +} + +/* +=========== +SelectRandomFurthestSpawnPoint + +Chooses a player start, deathmatch start, etc +============ +*/ +gentity_t *SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles ) { + gentity_t *spot; + vec3_t delta; + float dist; + float list_dist[64]; + gentity_t *list_spot[64]; + int numSpots, rnd, i, j; + + numSpots = 0; + spot = NULL; + + while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { + if ( SpotWouldTelefrag( spot ) ) { + continue; + } + VectorSubtract( spot->s.origin, avoidPoint, delta ); + dist = VectorLength( delta ); + for (i = 0; i < numSpots; i++) { + if ( dist > list_dist[i] ) { + if ( numSpots >= 64 ) + numSpots = 64-1; + for (j = numSpots; j > i; j--) { + list_dist[j] = list_dist[j-1]; + list_spot[j] = list_spot[j-1]; + } + list_dist[i] = dist; + list_spot[i] = spot; + numSpots++; + if (numSpots > 64) + numSpots = 64; + break; + } + } + if (i >= numSpots && numSpots < 64) { + list_dist[numSpots] = dist; + list_spot[numSpots] = spot; + numSpots++; + } + } + if (!numSpots) { + spot = G_Find( NULL, FOFS(classname), "info_player_deathmatch"); + if (!spot) + G_Error( "Couldn't find a spawn point" ); + VectorCopy (spot->s.origin, origin); + origin[2] += 9; + VectorCopy (spot->s.angles, angles); + return spot; + } + + // select a random spot from the spawn points furthest away + rnd = random() * (numSpots / 2); + + VectorCopy (list_spot[rnd]->s.origin, origin); + origin[2] += 9; + VectorCopy (list_spot[rnd]->s.angles, angles); + + return list_spot[rnd]; +} + +/* +=========== +SelectSpawnPoint + +Chooses a player start, deathmatch start, etc +============ +*/ +gentity_t *SelectSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles ) { + return SelectRandomFurthestSpawnPoint( avoidPoint, origin, angles ); + + /* + gentity_t *spot; + gentity_t *nearestSpot; + + nearestSpot = SelectNearestDeathmatchSpawnPoint( avoidPoint ); + + spot = SelectRandomDeathmatchSpawnPoint ( ); + if ( spot == nearestSpot ) { + // roll again if it would be real close to point of death + spot = SelectRandomDeathmatchSpawnPoint ( ); + if ( spot == nearestSpot ) { + // last try + spot = SelectRandomDeathmatchSpawnPoint ( ); + } + } + + // find a single player start spot + if (!spot) { + G_Error( "Couldn't find a spawn point" ); + } + + VectorCopy (spot->s.origin, origin); + origin[2] += 9; + VectorCopy (spot->s.angles, angles); + + return spot; + */ +} + +/* +=========== +SelectInitialSpawnPoint + +Try to find a spawn point marked 'initial', otherwise +use normal spawn selection. +============ +*/ +gentity_t *SelectInitialSpawnPoint( vec3_t origin, vec3_t angles ) { + gentity_t *spot; + + spot = NULL; + while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { + if ( spot->spawnflags & 1 ) { + break; + } + } + + if ( !spot || SpotWouldTelefrag( spot ) ) { + return SelectSpawnPoint( vec3_origin, origin, angles ); + } + + VectorCopy (spot->s.origin, origin); + origin[2] += 9; + VectorCopy (spot->s.angles, angles); + + return spot; +} + +/* +=========== +SelectSpectatorSpawnPoint + +============ +*/ +gentity_t *SelectSpectatorSpawnPoint( vec3_t origin, vec3_t angles ) { + FindIntermissionPoint(); + + VectorCopy( level.intermission_origin, origin ); + VectorCopy( level.intermission_angle, angles ); + + return NULL; +} + +/* +======================================================================= + +BODYQUE + +======================================================================= +*/ + +/* +=============== +InitBodyQue +=============== +*/ +void InitBodyQue (void) { + int i; + gentity_t *ent; + + level.bodyQueIndex = 0; + for (i=0; iclassname = "bodyque"; + ent->neverFree = qtrue; + level.bodyQue[i] = ent; + } +} + +/* +============= +BodySink + +After sitting around for five seconds, fall into the ground and dissapear +============= +*/ +void BodySink( gentity_t *ent ) { + if ( level.time - ent->timestamp > 6500 ) { + // the body ques are never actually freed, they are just unlinked + trap_UnlinkEntity( ent ); + ent->physicsObject = qfalse; + return; + } + ent->nextthink = level.time + 100; + ent->s.pos.trBase[2] -= 1; +} + +/* +============= +CopyToBodyQue + +A player is respawning, so make an entity that looks +just like the existing corpse to leave behind. +============= +*/ +void CopyToBodyQue( gentity_t *ent ) { +#ifdef MISSIONPACK + gentity_t *e; + int i; +#endif + gentity_t *body; + int contents; + + trap_UnlinkEntity (ent); + + // if client is in a nodrop area, don't leave the body + contents = trap_PointContents( ent->s.origin, -1 ); + if ( contents & CONTENTS_NODROP ) { + return; + } + + // grab a body que and cycle to the next one + body = level.bodyQue[ level.bodyQueIndex ]; + level.bodyQueIndex = (level.bodyQueIndex + 1) % BODY_QUEUE_SIZE; + + trap_UnlinkEntity (body); + + body->s = ent->s; + body->s.eFlags = EF_DEAD; // clear EF_TALK, etc +#ifdef MISSIONPACK + if ( ent->s.eFlags & EF_KAMIKAZE ) { + body->s.eFlags |= EF_KAMIKAZE; + + // check if there is a kamikaze timer around for this owner + for (i = 0; i < MAX_GENTITIES; i++) { + e = &g_entities[i]; + if (!e->inuse) + continue; + if (e->activator != ent) + continue; + if (strcmp(e->classname, "kamikaze timer")) + continue; + e->activator = body; + break; + } + } +#endif + body->s.powerups = 0; // clear powerups + body->s.loopSound = 0; // clear lava burning + body->s.number = body - g_entities; + body->timestamp = level.time; + body->physicsObject = qtrue; + body->physicsBounce = 0; // don't bounce + if ( body->s.groundEntityNum == ENTITYNUM_NONE ) { + body->s.pos.trType = TR_GRAVITY; + body->s.pos.trTime = level.time; + VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta ); + } else { + body->s.pos.trType = TR_STATIONARY; + } + body->s.event = 0; + + // change the animation to the last-frame only, so the sequence + // doesn't repeat anew for the body + switch ( body->s.legsAnim & ~ANIM_TOGGLEBIT ) { + case BOTH_DEATH1: + case BOTH_DEAD1: + body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1; + break; + case BOTH_DEATH2: + case BOTH_DEAD2: + body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2; + break; + case BOTH_DEATH3: + case BOTH_DEAD3: + default: + body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3; + break; + } + + body->r.svFlags = ent->r.svFlags; + VectorCopy (ent->r.mins, body->r.mins); + VectorCopy (ent->r.maxs, body->r.maxs); + VectorCopy (ent->r.absmin, body->r.absmin); + VectorCopy (ent->r.absmax, body->r.absmax); + + body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP; + body->r.contents = CONTENTS_CORPSE; + body->r.ownerNum = ent->s.number; + + body->nextthink = level.time + 5000; + body->think = BodySink; + + body->die = body_die; + + // don't take more damage if already gibbed + if ( ent->health <= GIB_HEALTH ) { + body->takedamage = qfalse; + } else { + body->takedamage = qtrue; + } + + + VectorCopy ( body->s.pos.trBase, body->r.currentOrigin ); + trap_LinkEntity (body); +} + +//====================================================================== + + +/* +================== +SetClientViewAngle + +================== +*/ +void SetClientViewAngle( gentity_t *ent, vec3_t angle ) { + int i; + + // set the delta angle + for (i=0 ; i<3 ; i++) { + int cmdAngle; + + cmdAngle = ANGLE2SHORT(angle[i]); + ent->client->ps.delta_angles[i] = cmdAngle - ent->client->pers.cmd.angles[i]; + } + VectorCopy( angle, ent->s.angles ); + VectorCopy (ent->s.angles, ent->client->ps.viewangles); +} + +/* +================ +respawn +================ +*/ +void respawn( gentity_t *ent ) { + gentity_t *tent; + + CopyToBodyQue (ent); + ClientSpawn(ent); + + // add a teleportation effect + tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN ); + tent->s.clientNum = ent->s.clientNum; +} + +/* +================ +TeamCount + +Returns number of players on a team +================ +*/ +team_t TeamCount( int ignoreClientNum, int team ) { + int i; + int count = 0; + + for ( i = 0 ; i < level.maxclients ; i++ ) { + if ( i == ignoreClientNum ) { + continue; + } + if ( level.clients[i].pers.connected == CON_DISCONNECTED ) { + continue; + } + if ( level.clients[i].sess.sessionTeam == team ) { + count++; + } + } + + return count; +} + +/* +================ +TeamLeader + +Returns the client number of the team leader +================ +*/ +int TeamLeader( int team ) { + int i; + + for ( i = 0 ; i < level.maxclients ; i++ ) { + if ( level.clients[i].pers.connected == CON_DISCONNECTED ) { + continue; + } + if ( level.clients[i].sess.sessionTeam == team ) { + if ( level.clients[i].sess.teamLeader ) + return i; + } + } + + return -1; +} + + +/* +================ +PickTeam + +================ +*/ +team_t PickTeam( int ignoreClientNum ) { + int counts[TEAM_NUM_TEAMS]; + + counts[TEAM_BLUE] = TeamCount( ignoreClientNum, TEAM_BLUE ); + counts[TEAM_RED] = TeamCount( ignoreClientNum, TEAM_RED ); + + if ( counts[TEAM_BLUE] > counts[TEAM_RED] ) { + return TEAM_RED; + } + if ( counts[TEAM_RED] > counts[TEAM_BLUE] ) { + return TEAM_BLUE; + } + // equal team count, so join the team with the lowest score + if ( level.teamScores[TEAM_BLUE] > level.teamScores[TEAM_RED] ) { + return TEAM_RED; + } + return TEAM_BLUE; +} + +/* +=========== +ForceClientSkin + +Forces a client's skin (for teamplay) +=========== +*/ +/* +static void ForceClientSkin( gclient_t *client, char *model, const char *skin ) { + char *p; + + if ((p = Q_strrchr(model, '/')) != 0) { + *p = 0; + } + + Q_strcat(model, MAX_QPATH, "/"); + Q_strcat(model, MAX_QPATH, skin); +} +*/ + +/* +=========== +ClientCheckName +============ +*/ +static void ClientCleanName( const char *in, char *out, int outSize ) { + int len, colorlessLen; + char ch; + char *p; + int spaces; + + //save room for trailing null byte + outSize--; + + len = 0; + colorlessLen = 0; + p = out; + *p = 0; + spaces = 0; + + while( 1 ) { + ch = *in++; + if( !ch ) { + break; + } + + // don't allow leading spaces + if( colorlessLen == 0 && ch == ' ' ) { + continue; + } + + // check colors + if( ch == Q_COLOR_ESCAPE ) { + // solo trailing carat is not a color prefix + if( !*in ) { + break; + } + + // don't allow black in a name, period + if( ColorIndex(*in) == 0 ) { + in++; + continue; + } + + // make sure room in dest for both chars + if( len > outSize - 2 ) { + break; + } + + *out++ = ch; + *out++ = *in++; + len += 2; + continue; + } + + // don't allow too many consecutive spaces + // don't count spaces in colorlessLen + if( ch == ' ' ) { + spaces++; + if( spaces > 3 ) { + continue; + } + *out++ = ch; + len++; + continue; + } + else { + spaces = 0; + } + + if( len > outSize - 1 ) { + break; + } + + *out++ = ch; + colorlessLen++; + len++; + } + *out = 0; + + // don't allow empty names + if( *p == 0 || colorlessLen == 0 ) { + Q_strncpyz( p, "UnnamedPlayer", outSize ); + } +} + + +/* +=========== +ClientUserInfoChanged + +Called from ClientConnect when the player first connects and +directly by the server system when the player updates a userinfo variable. + +The game can override any of the settings and call trap_SetUserinfo +if desired. +============ +*/ +void ClientUserinfoChanged( int clientNum ) { + gentity_t *ent; + int teamTask, teamLeader, team, health; + char *s; + char model[MAX_QPATH]; + char headModel[MAX_QPATH]; + char oldname[MAX_STRING_CHARS]; + gclient_t *client; + char c1[MAX_INFO_STRING]; + char c2[MAX_INFO_STRING]; + char redTeam[MAX_INFO_STRING]; + char blueTeam[MAX_INFO_STRING]; + char userinfo[MAX_INFO_STRING]; + + ent = g_entities + clientNum; + client = ent->client; + + trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); + + // check for malformed or illegal info strings + if ( !Info_Validate(userinfo) ) { + strcpy (userinfo, "\\name\\badinfo"); + } + + // check for local client + s = Info_ValueForKey( userinfo, "ip" ); + if ( !strcmp( s, "localhost" ) ) { + client->pers.localClient = qtrue; + } + + // check the item prediction + s = Info_ValueForKey( userinfo, "cg_predictItems" ); + if ( !atoi( s ) ) { + client->pers.predictItemPickup = qfalse; + } else { + client->pers.predictItemPickup = qtrue; + } + + // set name + Q_strncpyz ( oldname, client->pers.netname, sizeof( oldname ) ); + s = Info_ValueForKey (userinfo, "name"); + ClientCleanName( s, client->pers.netname, sizeof(client->pers.netname) ); + + if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { + if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) { + Q_strncpyz( client->pers.netname, "scoreboard", sizeof(client->pers.netname) ); + } + } + + if ( client->pers.connected == CON_CONNECTED ) { + if ( strcmp( oldname, client->pers.netname ) ) { + trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, + client->pers.netname) ); + } + } + + // set max health +#ifdef MISSIONPACK + if (client->ps.powerups[PW_GUARD]) { + client->pers.maxHealth = 200; + } else { + health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); + client->pers.maxHealth = health; + if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { + client->pers.maxHealth = 100; + } + } +#else + health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); + client->pers.maxHealth = health; + if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { + client->pers.maxHealth = 100; + } +#endif + client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; + + // set model + if( g_gametype.integer >= GT_TEAM ) { + Q_strncpyz( model, Info_ValueForKey (userinfo, "team_model"), sizeof( model ) ); + Q_strncpyz( headModel, Info_ValueForKey (userinfo, "team_headmodel"), sizeof( headModel ) ); + } else { + Q_strncpyz( model, Info_ValueForKey (userinfo, "model"), sizeof( model ) ); + Q_strncpyz( headModel, Info_ValueForKey (userinfo, "headmodel"), sizeof( headModel ) ); + } + + // bots set their team a few frames later + if (g_gametype.integer >= GT_TEAM && g_entities[clientNum].r.svFlags & SVF_BOT) { + s = Info_ValueForKey( userinfo, "team" ); + if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) { + team = TEAM_RED; + } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) { + team = TEAM_BLUE; + } else { + // pick the team with the least number of players + team = PickTeam( clientNum ); + } + } + else { + team = client->sess.sessionTeam; + } + +/* NOTE: all client side now + + // team + switch( team ) { + case TEAM_RED: + ForceClientSkin(client, model, "red"); +// ForceClientSkin(client, headModel, "red"); + break; + case TEAM_BLUE: + ForceClientSkin(client, model, "blue"); +// ForceClientSkin(client, headModel, "blue"); + break; + } + // don't ever use a default skin in teamplay, it would just waste memory + // however bots will always join a team but they spawn in as spectator + if ( g_gametype.integer >= GT_TEAM && team == TEAM_SPECTATOR) { + ForceClientSkin(client, model, "red"); +// ForceClientSkin(client, headModel, "red"); + } +*/ + +#ifdef MISSIONPACK + if (g_gametype.integer >= GT_TEAM) { + client->pers.teamInfo = qtrue; + } else { + s = Info_ValueForKey( userinfo, "teamoverlay" ); + if ( ! *s || atoi( s ) != 0 ) { + client->pers.teamInfo = qtrue; + } else { + client->pers.teamInfo = qfalse; + } + } +#else + // teamInfo + s = Info_ValueForKey( userinfo, "teamoverlay" ); + if ( ! *s || atoi( s ) != 0 ) { + client->pers.teamInfo = qtrue; + } else { + client->pers.teamInfo = qfalse; + } +#endif + /* + s = Info_ValueForKey( userinfo, "cg_pmove_fixed" ); + if ( !*s || atoi( s ) == 0 ) { + client->pers.pmoveFixed = qfalse; + } + else { + client->pers.pmoveFixed = qtrue; + } + */ + + // team task (0 = none, 1 = offence, 2 = defence) + teamTask = atoi(Info_ValueForKey(userinfo, "teamtask")); + // team Leader (1 = leader, 0 is normal player) + teamLeader = client->sess.teamLeader; + + // colors + strcpy(c1, Info_ValueForKey( userinfo, "color1" )); + strcpy(c2, Info_ValueForKey( userinfo, "color2" )); + + strcpy(redTeam, Info_ValueForKey( userinfo, "g_redteam" )); + strcpy(blueTeam, Info_ValueForKey( userinfo, "g_blueteam" )); + + // send over a subset of the userinfo keys so other clients can + // print scoreboards, display models, and play custom sounds + if ( ent->r.svFlags & SVF_BOT ) { + s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tt\\%d\\tl\\%d", + client->pers.netname, team, model, headModel, c1, c2, + client->pers.maxHealth, client->sess.wins, client->sess.losses, + Info_ValueForKey( userinfo, "skill" ), teamTask, teamLeader ); + } else { + s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\g_redteam\\%s\\g_blueteam\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\tl\\%d", + client->pers.netname, client->sess.sessionTeam, model, headModel, redTeam, blueTeam, c1, c2, + client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader); + } + + trap_SetConfigstring( CS_PLAYERS+clientNum, s ); + + // this is not the userinfo, more like the configstring actually + G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, s ); +} + + +/* +=========== +ClientConnect + +Called when a player begins connecting to the server. +Called again for every map change or tournement restart. + +The session information will be valid after exit. + +Return NULL if the client should be allowed, otherwise return +a string with the reason for denial. + +Otherwise, the client will be sent the current gamestate +and will eventually get to ClientBegin. + +firstTime will be qtrue the very first time a client connects +to the server machine, but qfalse on map changes and tournement +restarts. +============ +*/ +char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) { + char *value; +// char *areabits; + gclient_t *client; + char userinfo[MAX_INFO_STRING]; + gentity_t *ent; + + ent = &g_entities[ clientNum ]; + + trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); + + // IP filtering + // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=500 + // recommanding PB based IP / GUID banning, the builtin system is pretty limited + // check to see if they are on the banned IP list + value = Info_ValueForKey (userinfo, "ip"); + if ( G_FilterPacket( value ) ) { + return "You are banned from this server."; + } + + // we don't check password for bots and local client + // NOTE: local client <-> "ip" "localhost" + // this means this client is not running in our current process + if ( !isBot && (strcmp(value, "localhost") != 0)) { + // check for a password + value = Info_ValueForKey (userinfo, "password"); + if ( g_password.string[0] && Q_stricmp( g_password.string, "none" ) && + strcmp( g_password.string, value) != 0) { + return "Invalid password"; + } + } + + // they can connect + ent->client = level.clients + clientNum; + client = ent->client; + +// areabits = client->areabits; + + memset( client, 0, sizeof(*client) ); + + client->pers.connected = CON_CONNECTING; + + // read or initialize the session data + if ( firstTime || level.newSession ) { + G_InitSessionData( client, userinfo ); + } + G_ReadSessionData( client ); + + if( isBot ) { + ent->r.svFlags |= SVF_BOT; + ent->inuse = qtrue; + if( !G_BotConnect( clientNum, !firstTime ) ) { + return "BotConnectfailed"; + } + } + + // get and distribute relevent paramters + G_LogPrintf( "ClientConnect: %i\n", clientNum ); + ClientUserinfoChanged( clientNum ); + + // don't do the "xxx connected" messages if they were caried over from previous level + if ( firstTime ) { + trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname) ); + } + + if ( g_gametype.integer >= GT_TEAM && + client->sess.sessionTeam != TEAM_SPECTATOR ) { + BroadcastTeamChange( client, -1 ); + } + + // count current clients and rank for scoreboard + CalculateRanks(); + + // for statistics +// client->areabits = areabits; +// if ( !client->areabits ) +// client->areabits = G_Alloc( (trap_AAS_PointReachabilityAreaIndex( NULL ) + 7) / 8 ); + + return NULL; +} + +/* +=========== +ClientBegin + +called when a client has finished connecting, and is ready +to be placed into the level. This will happen every level load, +and on transition between teams, but doesn't happen on respawns +============ +*/ +void ClientBegin( int clientNum ) { + gentity_t *ent; + gclient_t *client; + gentity_t *tent; + int flags; + + ent = g_entities + clientNum; + + client = level.clients + clientNum; + + if ( ent->r.linked ) { + trap_UnlinkEntity( ent ); + } + G_InitGentity( ent ); + ent->touch = 0; + ent->pain = 0; + ent->client = client; + + client->pers.connected = CON_CONNECTED; + client->pers.enterTime = level.time; + client->pers.teamState.state = TEAM_BEGIN; + + // save eflags around this, because changing teams will + // cause this to happen with a valid entity, and we + // want to make sure the teleport bit is set right + // so the viewpoint doesn't interpolate through the + // world to the new position + flags = client->ps.eFlags; + memset( &client->ps, 0, sizeof( client->ps ) ); + client->ps.eFlags = flags; + + // locate ent at a spawn point + ClientSpawn( ent ); + + if ( client->sess.sessionTeam != TEAM_SPECTATOR ) { + // send event + tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN ); + tent->s.clientNum = ent->s.clientNum; + + if ( g_gametype.integer != GT_TOURNAMENT ) { + trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " entered the game\n\"", client->pers.netname) ); + } + } + G_LogPrintf( "ClientBegin: %i\n", clientNum ); + + // count current clients and rank for scoreboard + CalculateRanks(); +} + +/* +=========== +ClientSpawn + +Called every time a client is placed fresh in the world: +after the first ClientBegin, and after each respawn +Initializes all non-persistant parts of playerState +============ +*/ +void ClientSpawn(gentity_t *ent) { + int index; + vec3_t spawn_origin, spawn_angles; + gclient_t *client; + int i; + clientPersistant_t saved; + clientSession_t savedSess; + int persistant[MAX_PERSISTANT]; + gentity_t *spawnPoint; + int flags; + int savedPing; +// char *savedAreaBits; + int accuracy_hits, accuracy_shots; + int eventSequence; + char userinfo[MAX_INFO_STRING]; + + index = ent - g_entities; + client = ent->client; + + // find a spawn point + // do it before setting health back up, so farthest + // ranging doesn't count this client + if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { + spawnPoint = SelectSpectatorSpawnPoint ( + spawn_origin, spawn_angles); + } else if (g_gametype.integer >= GT_CTF ) { + // all base oriented team games use the CTF spawn points + spawnPoint = SelectCTFSpawnPoint ( + client->sess.sessionTeam, + client->pers.teamState.state, + spawn_origin, spawn_angles); + } else { + do { + // the first spawn should be at a good looking spot + if ( !client->pers.initialSpawn && client->pers.localClient ) { + client->pers.initialSpawn = qtrue; + spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles ); + } else { + // don't spawn near existing origin if possible + spawnPoint = SelectSpawnPoint ( + client->ps.origin, + spawn_origin, spawn_angles); + } + + // Tim needs to prevent bots from spawning at the initial point + // on q3dm0... + if ( ( spawnPoint->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) { + continue; // try again + } + // just to be symetric, we have a nohumans option... + if ( ( spawnPoint->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) { + continue; // try again + } + + break; + + } while ( 1 ); + } + client->pers.teamState.state = TEAM_ACTIVE; + + // always clear the kamikaze flag + ent->s.eFlags &= ~EF_KAMIKAZE; + + // toggle the teleport bit so the client knows to not lerp + // and never clear the voted flag + flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED); + flags ^= EF_TELEPORT_BIT; + + // clear everything but the persistant data + + saved = client->pers; + savedSess = client->sess; + savedPing = client->ps.ping; +// savedAreaBits = client->areabits; + accuracy_hits = client->accuracy_hits; + accuracy_shots = client->accuracy_shots; + for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { + persistant[i] = client->ps.persistant[i]; + } + eventSequence = client->ps.eventSequence; + + Com_Memset (client, 0, sizeof(*client)); + + client->pers = saved; + client->sess = savedSess; + client->ps.ping = savedPing; +// client->areabits = savedAreaBits; + client->accuracy_hits = accuracy_hits; + client->accuracy_shots = accuracy_shots; + client->lastkilled_client = -1; + + for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { + client->ps.persistant[i] = persistant[i]; + } + client->ps.eventSequence = eventSequence; + // increment the spawncount so the client will detect the respawn + client->ps.persistant[PERS_SPAWN_COUNT]++; + client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam; + + client->airOutTime = level.time + 12000; + + trap_GetUserinfo( index, userinfo, sizeof(userinfo) ); + // set max health + client->pers.maxHealth = atoi( Info_ValueForKey( userinfo, "handicap" ) ); + if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { + client->pers.maxHealth = 100; + } + // clear entity values + client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; + client->ps.eFlags = flags; + + ent->s.groundEntityNum = ENTITYNUM_NONE; + ent->client = &level.clients[index]; + ent->takedamage = qtrue; + ent->inuse = qtrue; + ent->classname = "player"; + ent->r.contents = CONTENTS_BODY; + ent->clipmask = MASK_PLAYERSOLID; + ent->die = player_die; + ent->waterlevel = 0; + ent->watertype = 0; + ent->flags = 0; + + VectorCopy (playerMins, ent->r.mins); + VectorCopy (playerMaxs, ent->r.maxs); + + client->ps.clientNum = index; + + client->ps.stats[STAT_WEAPONS] = ( 1 << WP_MACHINEGUN ); + if ( g_gametype.integer == GT_TEAM ) { + client->ps.ammo[WP_MACHINEGUN] = 50; + } else { + client->ps.ammo[WP_MACHINEGUN] = 100; + } + + client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GAUNTLET ); + client->ps.ammo[WP_GAUNTLET] = -1; + client->ps.ammo[WP_GRAPPLING_HOOK] = -1; + + // health will count down towards max_health + ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] + 25; + + G_SetOrigin( ent, spawn_origin ); + VectorCopy( spawn_origin, client->ps.origin ); + + // the respawned flag will be cleared after the attack and jump keys come up + client->ps.pm_flags |= PMF_RESPAWNED; + + trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd ); + SetClientViewAngle( ent, spawn_angles ); + + if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { + + } else { + G_KillBox( ent ); + trap_LinkEntity (ent); + + // force the base weapon up + client->ps.weapon = WP_MACHINEGUN; + client->ps.weaponstate = WEAPON_READY; + + } + + // don't allow full run speed for a bit + client->ps.pm_flags |= PMF_TIME_KNOCKBACK; + client->ps.pm_time = 100; + + client->respawnTime = level.time; + client->inactivityTime = level.time + g_inactivity.integer * 1000; + client->latched_buttons = 0; + + // set default animations + client->ps.torsoAnim = TORSO_STAND; + client->ps.legsAnim = LEGS_IDLE; + + if ( level.intermissiontime ) { + MoveClientToIntermission( ent ); + } else { + // fire the targets of the spawn point + G_UseTargets( spawnPoint, ent ); + + // select the highest weapon number available, after any + // spawn given items have fired + client->ps.weapon = 1; + for ( i = WP_NUM_WEAPONS - 1 ; i > 0 ; i-- ) { + if ( client->ps.stats[STAT_WEAPONS] & ( 1 << i ) ) { + client->ps.weapon = i; + break; + } + } + } + + // run a client frame to drop exactly to the floor, + // initialize animations and other things + client->ps.commandTime = level.time - 100; + ent->client->pers.cmd.serverTime = level.time; + ClientThink( ent-g_entities ); + + // positively link the client, even if the command times are weird + if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { + BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); + VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); + trap_LinkEntity( ent ); + } + + // run the presend to set anything else + ClientEndFrame( ent ); + + // clear entity state values + BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); +} + + +/* +=========== +ClientDisconnect + +Called when a player drops from the server. +Will not be called between levels. + +This should NOT be called directly by any game logic, +call trap_DropClient(), which will call this and do +server system housekeeping. +============ +*/ +void ClientDisconnect( int clientNum ) { + gentity_t *ent; + gentity_t *tent; + int i; + + // cleanup if we are kicking a bot that + // hasn't spawned yet + G_RemoveQueuedBotBegin( clientNum ); + + ent = g_entities + clientNum; + if ( !ent->client ) { + return; + } + + // stop any following clients + for ( i = 0 ; i < level.maxclients ; i++ ) { + if ( level.clients[i].sess.sessionTeam == TEAM_SPECTATOR + && level.clients[i].sess.spectatorState == SPECTATOR_FOLLOW + && level.clients[i].sess.spectatorClient == clientNum ) { + StopFollowing( &g_entities[i] ); + } + } + + // send effect if they were completely connected + if ( ent->client->pers.connected == CON_CONNECTED + && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { + tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_OUT ); + tent->s.clientNum = ent->s.clientNum; + + // They don't get to take powerups with them! + // Especially important for stuff like CTF flags + TossClientItems( ent ); +#ifdef MISSIONPACK + TossClientPersistantPowerups( ent ); + if( g_gametype.integer == GT_HARVESTER ) { + TossClientCubes( ent ); + } +#endif + + } + + G_LogPrintf( "ClientDisconnect: %i\n", clientNum ); + + // if we are playing in tourney mode and losing, give a win to the other player + if ( (g_gametype.integer == GT_TOURNAMENT ) + && !level.intermissiontime + && !level.warmupTime && level.sortedClients[1] == clientNum ) { + level.clients[ level.sortedClients[0] ].sess.wins++; + ClientUserinfoChanged( level.sortedClients[0] ); + } + + if( g_gametype.integer == GT_TOURNAMENT && + ent->client->sess.sessionTeam == TEAM_FREE && + level.intermissiontime ) { + + trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" ); + level.restarted = qtrue; + level.changemap = NULL; + level.intermissiontime = 0; + } + + trap_UnlinkEntity (ent); + ent->s.modelindex = 0; + ent->inuse = qfalse; + ent->classname = "disconnected"; + ent->client->pers.connected = CON_DISCONNECTED; + ent->client->ps.persistant[PERS_TEAM] = TEAM_FREE; + ent->client->sess.sessionTeam = TEAM_FREE; + + trap_SetConfigstring( CS_PLAYERS + clientNum, ""); + + CalculateRanks(); + + if ( ent->r.svFlags & SVF_BOT ) { + BotAIShutdownClient( clientNum, qfalse ); + } +} + + diff --git a/reaction/engine/code/game/g_cmds.c b/reaction/engine/code/game/g_cmds.c new file mode 100644 index 00000000..2569b3b8 --- /dev/null +++ b/reaction/engine/code/game/g_cmds.c @@ -0,0 +1,1685 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena 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 2 of the License, +or (at your option) any later version. + +Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +#include "g_local.h" + +#include "../../ui/menudef.h" // for the voice chats + +/* +================== +DeathmatchScoreboardMessage + +================== +*/ +void DeathmatchScoreboardMessage( gentity_t *ent ) { + char entry[1024]; + char string[1400]; + int stringlength; + int i, j; + gclient_t *cl; + int numSorted, scoreFlags, accuracy, perfect; + + // send the latest information on all clients + string[0] = 0; + stringlength = 0; + scoreFlags = 0; + + numSorted = level.numConnectedClients; + + for (i=0 ; i < numSorted ; i++) { + int ping; + + cl = &level.clients[level.sortedClients[i]]; + + if ( cl->pers.connected == CON_CONNECTING ) { + ping = -1; + } else { + ping = cl->ps.ping < 999 ? cl->ps.ping : 999; + } + + if( cl->accuracy_shots ) { + accuracy = cl->accuracy_hits * 100 / cl->accuracy_shots; + } + else { + accuracy = 0; + } + perfect = ( cl->ps.persistant[PERS_RANK] == 0 && cl->ps.persistant[PERS_KILLED] == 0 ) ? 1 : 0; + + Com_sprintf (entry, sizeof(entry), + " %i %i %i %i %i %i %i %i %i %i %i %i %i %i", level.sortedClients[i], + cl->ps.persistant[PERS_SCORE], ping, (level.time - cl->pers.enterTime)/60000, + scoreFlags, g_entities[level.sortedClients[i]].s.powerups, accuracy, + cl->ps.persistant[PERS_IMPRESSIVE_COUNT], + cl->ps.persistant[PERS_EXCELLENT_COUNT], + cl->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], + cl->ps.persistant[PERS_DEFEND_COUNT], + cl->ps.persistant[PERS_ASSIST_COUNT], + perfect, + cl->ps.persistant[PERS_CAPTURES]); + j = strlen(entry); + if (stringlength + j > 1024) + break; + strcpy (string + stringlength, entry); + stringlength += j; + } + + trap_SendServerCommand( ent-g_entities, va("scores %i %i %i%s", i, + level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE], + string ) ); +} + + +/* +================== +Cmd_Score_f + +Request current scoreboard information +================== +*/ +void Cmd_Score_f( gentity_t *ent ) { + DeathmatchScoreboardMessage( ent ); +} + + + +/* +================== +CheatsOk +================== +*/ +qboolean CheatsOk( gentity_t *ent ) { + if ( !g_cheats.integer ) { + trap_SendServerCommand( ent-g_entities, va("print \"Cheats are not enabled on this server.\n\"")); + return qfalse; + } + if ( ent->health <= 0 ) { + trap_SendServerCommand( ent-g_entities, va("print \"You must be alive to use this command.\n\"")); + return qfalse; + } + return qtrue; +} + + +/* +================== +ConcatArgs +================== +*/ +char *ConcatArgs( int start ) { + int i, c, tlen; + static char line[MAX_STRING_CHARS]; + int len; + char arg[MAX_STRING_CHARS]; + + len = 0; + c = trap_Argc(); + for ( i = start ; i < c ; i++ ) { + trap_Argv( i, arg, sizeof( arg ) ); + tlen = strlen( arg ); + if ( len + tlen >= MAX_STRING_CHARS - 1 ) { + break; + } + memcpy( line + len, arg, tlen ); + len += tlen; + if ( i != c - 1 ) { + line[len] = ' '; + len++; + } + } + + line[len] = 0; + + return line; +} + +/* +================== +ClientNumberFromString + +Returns a player number for either a number or name string +Returns -1 if invalid +================== +*/ +int ClientNumberFromString( gentity_t *to, char *s ) { + gclient_t *cl; + int idnum; + char cleanName[MAX_STRING_CHARS]; + + // numeric values are just slot numbers + if (s[0] >= '0' && s[0] <= '9') { + idnum = atoi( s ); + if ( idnum < 0 || idnum >= level.maxclients ) { + trap_SendServerCommand( to-g_entities, va("print \"Bad client slot: %i\n\"", idnum)); + return -1; + } + + cl = &level.clients[idnum]; + if ( cl->pers.connected != CON_CONNECTED ) { + trap_SendServerCommand( to-g_entities, va("print \"Client %i is not active\n\"", idnum)); + return -1; + } + return idnum; + } + + // check for a name match + for ( idnum=0,cl=level.clients ; idnum < level.maxclients ; idnum++,cl++ ) { + if ( cl->pers.connected != CON_CONNECTED ) { + continue; + } + Q_strncpyz(cleanName, cl->pers.netname, sizeof(cleanName)); + Q_CleanStr(cleanName); + if ( !Q_stricmp( cleanName, s ) ) { + return idnum; + } + } + + trap_SendServerCommand( to-g_entities, va("print \"User %s is not on the server\n\"", s)); + return -1; +} + +/* +================== +Cmd_Give_f + +Give items to a client +================== +*/ +void Cmd_Give_f (gentity_t *ent) +{ + char *name; + gitem_t *it; + int i; + qboolean give_all; + gentity_t *it_ent; + trace_t trace; + + if ( !CheatsOk( ent ) ) { + return; + } + + name = ConcatArgs( 1 ); + + if (Q_stricmp(name, "all") == 0) + give_all = qtrue; + else + give_all = qfalse; + + if (give_all || Q_stricmp( name, "health") == 0) + { + ent->health = ent->client->ps.stats[STAT_MAX_HEALTH]; + if (!give_all) + return; + } + + if (give_all || Q_stricmp(name, "weapons") == 0) + { + ent->client->ps.stats[STAT_WEAPONS] = (1 << WP_NUM_WEAPONS) - 1 - + ( 1 << WP_GRAPPLING_HOOK ) - ( 1 << WP_NONE ); + if (!give_all) + return; + } + + if (give_all || Q_stricmp(name, "ammo") == 0) + { + for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { + ent->client->ps.ammo[i] = 999; + } + if (!give_all) + return; + } + + if (give_all || Q_stricmp(name, "armor") == 0) + { + ent->client->ps.stats[STAT_ARMOR] = 200; + + if (!give_all) + return; + } + + if (Q_stricmp(name, "excellent") == 0) { + ent->client->ps.persistant[PERS_EXCELLENT_COUNT]++; + return; + } + if (Q_stricmp(name, "impressive") == 0) { + ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++; + return; + } + if (Q_stricmp(name, "gauntletaward") == 0) { + ent->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++; + return; + } + if (Q_stricmp(name, "defend") == 0) { + ent->client->ps.persistant[PERS_DEFEND_COUNT]++; + return; + } + if (Q_stricmp(name, "assist") == 0) { + ent->client->ps.persistant[PERS_ASSIST_COUNT]++; + return; + } + + // spawn a specific item right on the player + if ( !give_all ) { + it = BG_FindItem (name); + if (!it) { + return; + } + + it_ent = G_Spawn(); + VectorCopy( ent->r.currentOrigin, it_ent->s.origin ); + it_ent->classname = it->classname; + G_SpawnItem (it_ent, it); + FinishSpawningItem(it_ent ); + memset( &trace, 0, sizeof( trace ) ); + Touch_Item (it_ent, ent, &trace); + if (it_ent->inuse) { + G_FreeEntity( it_ent ); + } + } +} + + +/* +================== +Cmd_God_f + +Sets client to godmode + +argv(0) god +================== +*/ +void Cmd_God_f (gentity_t *ent) +{ + char *msg; + + if ( !CheatsOk( ent ) ) { + return; + } + + ent->flags ^= FL_GODMODE; + if (!(ent->flags & FL_GODMODE) ) + msg = "godmode OFF\n"; + else + msg = "godmode ON\n"; + + trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg)); +} + + +/* +================== +Cmd_Notarget_f + +Sets client to notarget + +argv(0) notarget +================== +*/ +void Cmd_Notarget_f( gentity_t *ent ) { + char *msg; + + if ( !CheatsOk( ent ) ) { + return; + } + + ent->flags ^= FL_NOTARGET; + if (!(ent->flags & FL_NOTARGET) ) + msg = "notarget OFF\n"; + else + msg = "notarget ON\n"; + + trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg)); +} + + +/* +================== +Cmd_Noclip_f + +argv(0) noclip +================== +*/ +void Cmd_Noclip_f( gentity_t *ent ) { + char *msg; + + if ( !CheatsOk( ent ) ) { + return; + } + + if ( ent->client->noclip ) { + msg = "noclip OFF\n"; + } else { + msg = "noclip ON\n"; + } + ent->client->noclip = !ent->client->noclip; + + trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg)); +} + + +/* +================== +Cmd_LevelShot_f + +This is just to help generate the level pictures +for the menus. It goes to the intermission immediately +and sends over a command to the client to resize the view, +hide the scoreboard, and take a special screenshot +================== +*/ +void Cmd_LevelShot_f( gentity_t *ent ) { + if ( !CheatsOk( ent ) ) { + return; + } + + // doesn't work in single player + if ( g_gametype.integer != 0 ) { + trap_SendServerCommand( ent-g_entities, + "print \"Must be in g_gametype 0 for levelshot\n\"" ); + return; + } + + BeginIntermission(); + trap_SendServerCommand( ent-g_entities, "clientLevelShot" ); +} + + +/* +================== +Cmd_LevelShot_f + +This is just to help generate the level pictures +for the menus. It goes to the intermission immediately +and sends over a command to the client to resize the view, +hide the scoreboard, and take a special screenshot +================== +*/ +void Cmd_TeamTask_f( gentity_t *ent ) { + char userinfo[MAX_INFO_STRING]; + char arg[MAX_TOKEN_CHARS]; + int task; + int client = ent->client - level.clients; + + if ( trap_Argc() != 2 ) { + return; + } + trap_Argv( 1, arg, sizeof( arg ) ); + task = atoi( arg ); + + trap_GetUserinfo(client, userinfo, sizeof(userinfo)); + Info_SetValueForKey(userinfo, "teamtask", va("%d", task)); + trap_SetUserinfo(client, userinfo); + ClientUserinfoChanged(client); +} + + + +/* +================= +Cmd_Kill_f +================= +*/ +void Cmd_Kill_f( gentity_t *ent ) { + if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { + return; + } + if (ent->health <= 0) { + return; + } + ent->flags &= ~FL_GODMODE; + ent->client->ps.stats[STAT_HEALTH] = ent->health = -999; + player_die (ent, ent, ent, 100000, MOD_SUICIDE); +} + +/* +================= +BroadCastTeamChange + +Let everyone know about a team change +================= +*/ +void BroadcastTeamChange( gclient_t *client, int oldTeam ) +{ + if ( client->sess.sessionTeam == TEAM_RED ) { + trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the red team.\n\"", + client->pers.netname) ); + } else if ( client->sess.sessionTeam == TEAM_BLUE ) { + trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the blue team.\n\"", + client->pers.netname)); + } else if ( client->sess.sessionTeam == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR ) { + trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the spectators.\n\"", + client->pers.netname)); + } else if ( client->sess.sessionTeam == TEAM_FREE ) { + trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the battle.\n\"", + client->pers.netname)); + } +} + +/* +================= +SetTeam +================= +*/ +void SetTeam( gentity_t *ent, char *s ) { + int team, oldTeam; + gclient_t *client; + int clientNum; + spectatorState_t specState; + int specClient; + int teamLeader; + + // + // see what change is requested + // + client = ent->client; + + clientNum = client - level.clients; + specClient = 0; + specState = SPECTATOR_NOT; + if ( !Q_stricmp( s, "scoreboard" ) || !Q_stricmp( s, "score" ) ) { + team = TEAM_SPECTATOR; + specState = SPECTATOR_SCOREBOARD; + } else if ( !Q_stricmp( s, "follow1" ) ) { + team = TEAM_SPECTATOR; + specState = SPECTATOR_FOLLOW; + specClient = -1; + } else if ( !Q_stricmp( s, "follow2" ) ) { + team = TEAM_SPECTATOR; + specState = SPECTATOR_FOLLOW; + specClient = -2; + } else if ( !Q_stricmp( s, "spectator" ) || !Q_stricmp( s, "s" ) ) { + team = TEAM_SPECTATOR; + specState = SPECTATOR_FREE; + } else if ( g_gametype.integer >= GT_TEAM ) { + // if running a team game, assign player to one of the teams + specState = SPECTATOR_NOT; + if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) { + team = TEAM_RED; + } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) { + team = TEAM_BLUE; + } else { + // pick the team with the least number of players + team = PickTeam( clientNum ); + } + + if ( g_teamForceBalance.integer ) { + int counts[TEAM_NUM_TEAMS]; + + counts[TEAM_BLUE] = TeamCount( ent->client->ps.clientNum, TEAM_BLUE ); + counts[TEAM_RED] = TeamCount( ent->client->ps.clientNum, TEAM_RED ); + + // We allow a spread of two + if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] > 1 ) { + trap_SendServerCommand( ent->client->ps.clientNum, + "cp \"Red team has too many players.\n\"" ); + return; // ignore the request + } + if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1 ) { + trap_SendServerCommand( ent->client->ps.clientNum, + "cp \"Blue team has too many players.\n\"" ); + return; // ignore the request + } + + // It's ok, the team we are switching to has less or same number of players + } + + } else { + // force them to spectators if there aren't any spots free + team = TEAM_FREE; + } + + // override decision if limiting the players + if ( (g_gametype.integer == GT_TOURNAMENT) + && level.numNonSpectatorClients >= 2 ) { + team = TEAM_SPECTATOR; + } else if ( g_maxGameClients.integer > 0 && + level.numNonSpectatorClients >= g_maxGameClients.integer ) { + team = TEAM_SPECTATOR; + } + + // + // decide if we will allow the change + // + oldTeam = client->sess.sessionTeam; + if ( team == oldTeam && team != TEAM_SPECTATOR ) { + return; + } + + // + // execute the team change + // + + // if the player was dead leave the body + if ( client->ps.stats[STAT_HEALTH] <= 0 ) { + CopyToBodyQue(ent); + } + + // he starts at 'base' + client->pers.teamState.state = TEAM_BEGIN; + if ( oldTeam != TEAM_SPECTATOR ) { + // Kill him (makes sure he loses flags, etc) + ent->flags &= ~FL_GODMODE; + ent->client->ps.stats[STAT_HEALTH] = ent->health = 0; + player_die (ent, ent, ent, 100000, MOD_SUICIDE); + + } + // they go to the end of the line for tournements + if ( team == TEAM_SPECTATOR ) { + client->sess.spectatorTime = level.time; + } + + client->sess.sessionTeam = team; + client->sess.spectatorState = specState; + client->sess.spectatorClient = specClient; + + client->sess.teamLeader = qfalse; + if ( team == TEAM_RED || team == TEAM_BLUE ) { + teamLeader = TeamLeader( team ); + // if there is no team leader or the team leader is a bot and this client is not a bot + if ( teamLeader == -1 || ( !(g_entities[clientNum].r.svFlags & SVF_BOT) && (g_entities[teamLeader].r.svFlags & SVF_BOT) ) ) { + SetLeader( team, clientNum ); + } + } + // make sure there is a team leader on the team the player came from + if ( oldTeam == TEAM_RED || oldTeam == TEAM_BLUE ) { + CheckTeamLeader( oldTeam ); + } + + BroadcastTeamChange( client, oldTeam ); + + // get and distribute relevent paramters + ClientUserinfoChanged( clientNum ); + + ClientBegin( clientNum ); +} + +/* +================= +StopFollowing + +If the client being followed leaves the game, or you just want to drop +to free floating spectator mode +================= +*/ +void StopFollowing( gentity_t *ent ) { + ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR; + ent->client->sess.sessionTeam = TEAM_SPECTATOR; + ent->client->sess.spectatorState = SPECTATOR_FREE; + ent->client->ps.pm_flags &= ~PMF_FOLLOW; + ent->r.svFlags &= ~SVF_BOT; + ent->client->ps.clientNum = ent - g_entities; +} + +/* +================= +Cmd_Team_f +================= +*/ +void Cmd_Team_f( gentity_t *ent ) { + int oldTeam; + char s[MAX_TOKEN_CHARS]; + + if ( trap_Argc() != 2 ) { + oldTeam = ent->client->sess.sessionTeam; + switch ( oldTeam ) { + case TEAM_BLUE: + trap_SendServerCommand( ent-g_entities, "print \"Blue team\n\"" ); + break; + case TEAM_RED: + trap_SendServerCommand( ent-g_entities, "print \"Red team\n\"" ); + break; + case TEAM_FREE: + trap_SendServerCommand( ent-g_entities, "print \"Free team\n\"" ); + break; + case TEAM_SPECTATOR: + trap_SendServerCommand( ent-g_entities, "print \"Spectator team\n\"" ); + break; + } + return; + } + + if ( ent->client->switchTeamTime > level.time ) { + trap_SendServerCommand( ent-g_entities, "print \"May not switch teams more than once per 5 seconds.\n\"" ); + return; + } + + // if they are playing a tournement game, count as a loss + if ( (g_gametype.integer == GT_TOURNAMENT ) + && ent->client->sess.sessionTeam == TEAM_FREE ) { + ent->client->sess.losses++; + } + + trap_Argv( 1, s, sizeof( s ) ); + + SetTeam( ent, s ); + + ent->client->switchTeamTime = level.time + 5000; +} + + +/* +================= +Cmd_Follow_f +================= +*/ +void Cmd_Follow_f( gentity_t *ent ) { + int i; + char arg[MAX_TOKEN_CHARS]; + + if ( trap_Argc() != 2 ) { + if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) { + StopFollowing( ent ); + } + return; + } + + trap_Argv( 1, arg, sizeof( arg ) ); + i = ClientNumberFromString( ent, arg ); + if ( i == -1 ) { + return; + } + + // can't follow self + if ( &level.clients[ i ] == ent->client ) { + return; + } + + // can't follow another spectator + if ( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR ) { + return; + } + + // if they are playing a tournement game, count as a loss + if ( (g_gametype.integer == GT_TOURNAMENT ) + && ent->client->sess.sessionTeam == TEAM_FREE ) { + ent->client->sess.losses++; + } + + // first set them to spectator + if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { + SetTeam( ent, "spectator" ); + } + + ent->client->sess.spectatorState = SPECTATOR_FOLLOW; + ent->client->sess.spectatorClient = i; +} + +/* +================= +Cmd_FollowCycle_f +================= +*/ +void Cmd_FollowCycle_f( gentity_t *ent, int dir ) { + int clientnum; + int original; + + // if they are playing a tournement game, count as a loss + if ( (g_gametype.integer == GT_TOURNAMENT ) + && ent->client->sess.sessionTeam == TEAM_FREE ) { + ent->client->sess.losses++; + } + // first set them to spectator + if ( ent->client->sess.spectatorState == SPECTATOR_NOT ) { + SetTeam( ent, "spectator" ); + } + + if ( dir != 1 && dir != -1 ) { + G_Error( "Cmd_FollowCycle_f: bad dir %i", dir ); + } + + clientnum = ent->client->sess.spectatorClient; + original = clientnum; + do { + clientnum += dir; + if ( clientnum >= level.maxclients ) { + clientnum = 0; + } + if ( clientnum < 0 ) { + clientnum = level.maxclients - 1; + } + + // can only follow connected clients + if ( level.clients[ clientnum ].pers.connected != CON_CONNECTED ) { + continue; + } + + // can't follow another spectator + if ( level.clients[ clientnum ].sess.sessionTeam == TEAM_SPECTATOR ) { + continue; + } + + // this is good, we can use it + ent->client->sess.spectatorClient = clientnum; + ent->client->sess.spectatorState = SPECTATOR_FOLLOW; + return; + } while ( clientnum != original ); + + // leave it where it was +} + + +/* +================== +G_Say +================== +*/ + +static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, const char *name, const char *message ) { + if (!other) { + return; + } + if (!other->inuse) { + return; + } + if (!other->client) { + return; + } + if ( other->client->pers.connected != CON_CONNECTED ) { + return; + } + if ( mode == SAY_TEAM && !OnSameTeam(ent, other) ) { + return; + } + // no chatting to players in tournements + if ( (g_gametype.integer == GT_TOURNAMENT ) + && other->client->sess.sessionTeam == TEAM_FREE + && ent->client->sess.sessionTeam != TEAM_FREE ) { + return; + } + + trap_SendServerCommand( other-g_entities, va("%s \"%s%c%c%s\"", + mode == SAY_TEAM ? "tchat" : "chat", + name, Q_COLOR_ESCAPE, color, message)); +} + +#define EC "\x19" + +void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) { + int j; + gentity_t *other; + int color; + char name[64]; + // don't let text be too long for malicious reasons + char text[MAX_SAY_TEXT]; + char location[64]; + + if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) { + mode = SAY_ALL; + } + + switch ( mode ) { + default: + case SAY_ALL: + G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText ); + Com_sprintf (name, sizeof(name), "%s%c%c"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); + color = COLOR_GREEN; + break; + case SAY_TEAM: + G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText ); + if (Team_GetLocationMsg(ent, location, sizeof(location))) + Com_sprintf (name, sizeof(name), EC"(%s%c%c"EC") (%s)"EC": ", + ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location); + else + Com_sprintf (name, sizeof(name), EC"(%s%c%c"EC")"EC": ", + ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); + color = COLOR_CYAN; + break; + case SAY_TELL: + if (target && g_gametype.integer >= GT_TEAM && + target->client->sess.sessionTeam == ent->client->sess.sessionTeam && + Team_GetLocationMsg(ent, location, sizeof(location))) + Com_sprintf (name, sizeof(name), EC"[%s%c%c"EC"] (%s)"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location ); + else + Com_sprintf (name, sizeof(name), EC"[%s%c%c"EC"]"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); + color = COLOR_MAGENTA; + break; + } + + Q_strncpyz( text, chatText, sizeof(text) ); + + if ( target ) { + G_SayTo( ent, target, mode, color, name, text ); + return; + } + + // echo the text to the console + if ( g_dedicated.integer ) { + G_Printf( "%s%s\n", name, text); + } + + // send it to all the apropriate clients + for (j = 0; j < level.maxclients; j++) { + other = &g_entities[j]; + G_SayTo( ent, other, mode, color, name, text ); + } +} + + +/* +================== +Cmd_Say_f +================== +*/ +static void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) { + char *p; + + if ( trap_Argc () < 2 && !arg0 ) { + return; + } + + if (arg0) + { + p = ConcatArgs( 0 ); + } + else + { + p = ConcatArgs( 1 ); + } + + G_Say( ent, NULL, mode, p ); +} + +/* +================== +Cmd_Tell_f +================== +*/ +static void Cmd_Tell_f( gentity_t *ent ) { + int targetNum; + gentity_t *target; + char *p; + char arg[MAX_TOKEN_CHARS]; + + if ( trap_Argc () < 2 ) { + return; + } + + trap_Argv( 1, arg, sizeof( arg ) ); + targetNum = atoi( arg ); + if ( targetNum < 0 || targetNum >= level.maxclients ) { + return; + } + + target = &g_entities[targetNum]; + if ( !target || !target->inuse || !target->client ) { + return; + } + + p = ConcatArgs( 2 ); + + G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p ); + G_Say( ent, target, SAY_TELL, p ); + // don't tell to the player self if it was already directed to this player + // also don't send the chat back to a bot + if ( ent != target && !(ent->r.svFlags & SVF_BOT)) { + G_Say( ent, ent, SAY_TELL, p ); + } +} + + +static void G_VoiceTo( gentity_t *ent, gentity_t *other, int mode, const char *id, qboolean voiceonly ) { + int color; + char *cmd; + + if (!other) { + return; + } + if (!other->inuse) { + return; + } + if (!other->client) { + return; + } + if ( mode == SAY_TEAM && !OnSameTeam(ent, other) ) { + return; + } + // no chatting to players in tournements + if ( (g_gametype.integer == GT_TOURNAMENT )) { + return; + } + + if (mode == SAY_TEAM) { + color = COLOR_CYAN; + cmd = "vtchat"; + } + else if (mode == SAY_TELL) { + color = COLOR_MAGENTA; + cmd = "vtell"; + } + else { + color = COLOR_GREEN; + cmd = "vchat"; + } + + trap_SendServerCommand( other-g_entities, va("%s %d %d %d %s", cmd, voiceonly, ent->s.number, color, id)); +} + +void G_Voice( gentity_t *ent, gentity_t *target, int mode, const char *id, qboolean voiceonly ) { + int j; + gentity_t *other; + + if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) { + mode = SAY_ALL; + } + + if ( target ) { + G_VoiceTo( ent, target, mode, id, voiceonly ); + return; + } + + // echo the text to the console + if ( g_dedicated.integer ) { + G_Printf( "voice: %s %s\n", ent->client->pers.netname, id); + } + + // send it to all the apropriate clients + for (j = 0; j < level.maxclients; j++) { + other = &g_entities[j]; + G_VoiceTo( ent, other, mode, id, voiceonly ); + } +} + +/* +================== +Cmd_Voice_f +================== +*/ +static void Cmd_Voice_f( gentity_t *ent, int mode, qboolean arg0, qboolean voiceonly ) { + char *p; + + if ( trap_Argc () < 2 && !arg0 ) { + return; + } + + if (arg0) + { + p = ConcatArgs( 0 ); + } + else + { + p = ConcatArgs( 1 ); + } + + G_Voice( ent, NULL, mode, p, voiceonly ); +} + +/* +================== +Cmd_VoiceTell_f +================== +*/ +static void Cmd_VoiceTell_f( gentity_t *ent, qboolean voiceonly ) { + int targetNum; + gentity_t *target; + char *id; + char arg[MAX_TOKEN_CHARS]; + + if ( trap_Argc () < 2 ) { + return; + } + + trap_Argv( 1, arg, sizeof( arg ) ); + targetNum = atoi( arg ); + if ( targetNum < 0 || targetNum >= level.maxclients ) { + return; + } + + target = &g_entities[targetNum]; + if ( !target || !target->inuse || !target->client ) { + return; + } + + id = ConcatArgs( 2 ); + + G_LogPrintf( "vtell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, id ); + G_Voice( ent, target, SAY_TELL, id, voiceonly ); + // don't tell to the player self if it was already directed to this player + // also don't send the chat back to a bot + if ( ent != target && !(ent->r.svFlags & SVF_BOT)) { + G_Voice( ent, ent, SAY_TELL, id, voiceonly ); + } +} + + +/* +================== +Cmd_VoiceTaunt_f +================== +*/ +static void Cmd_VoiceTaunt_f( gentity_t *ent ) { + gentity_t *who; + int i; + + if (!ent->client) { + return; + } + + // insult someone who just killed you + if (ent->enemy && ent->enemy->client && ent->enemy->client->lastkilled_client == ent->s.number) { + // i am a dead corpse + if (!(ent->enemy->r.svFlags & SVF_BOT)) { + G_Voice( ent, ent->enemy, SAY_TELL, VOICECHAT_DEATHINSULT, qfalse ); + } + if (!(ent->r.svFlags & SVF_BOT)) { + G_Voice( ent, ent, SAY_TELL, VOICECHAT_DEATHINSULT, qfalse ); + } + ent->enemy = NULL; + return; + } + // insult someone you just killed + if (ent->client->lastkilled_client >= 0 && ent->client->lastkilled_client != ent->s.number) { + who = g_entities + ent->client->lastkilled_client; + if (who->client) { + // who is the person I just killed + if (who->client->lasthurt_mod == MOD_GAUNTLET) { + if (!(who->r.svFlags & SVF_BOT)) { + G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse ); // and I killed them with a gauntlet + } + if (!(ent->r.svFlags & SVF_BOT)) { + G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse ); + } + } else { + if (!(who->r.svFlags & SVF_BOT)) { + G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLINSULT, qfalse ); // and I killed them with something else + } + if (!(ent->r.svFlags & SVF_BOT)) { + G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLINSULT, qfalse ); + } + } + ent->client->lastkilled_client = -1; + return; + } + } + + if (g_gametype.integer >= GT_TEAM) { + // praise a team mate who just got a reward + for(i = 0; i < MAX_CLIENTS; i++) { + who = g_entities + i; + if (who->client && who != ent && who->client->sess.sessionTeam == ent->client->sess.sessionTeam) { + if (who->client->rewardTime > level.time) { + if (!(who->r.svFlags & SVF_BOT)) { + G_Voice( ent, who, SAY_TELL, VOICECHAT_PRAISE, qfalse ); + } + if (!(ent->r.svFlags & SVF_BOT)) { + G_Voice( ent, ent, SAY_TELL, VOICECHAT_PRAISE, qfalse ); + } + return; + } + } + } + } + + // just say something + G_Voice( ent, NULL, SAY_ALL, VOICECHAT_TAUNT, qfalse ); +} + + + +static char *gc_orders[] = { + "hold your position", + "hold this position", + "come here", + "cover me", + "guard location", + "search and destroy", + "report" +}; + +void Cmd_GameCommand_f( gentity_t *ent ) { + int player; + int order; + char str[MAX_TOKEN_CHARS]; + + trap_Argv( 1, str, sizeof( str ) ); + player = atoi( str ); + trap_Argv( 2, str, sizeof( str ) ); + order = atoi( str ); + + if ( player < 0 || player >= MAX_CLIENTS ) { + return; + } + if ( order < 0 || order > sizeof(gc_orders)/sizeof(char *) ) { + return; + } + G_Say( ent, &g_entities[player], SAY_TELL, gc_orders[order] ); + G_Say( ent, ent, SAY_TELL, gc_orders[order] ); +} + +/* +================== +Cmd_Where_f +================== +*/ +void Cmd_Where_f( gentity_t *ent ) { + trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos( ent->s.origin ) ) ); +} + +static const char *gameNames[] = { + "Free For All", + "Tournament", + "Single Player", + "Team Deathmatch", + "Capture the Flag", + "One Flag CTF", + "Overload", + "Harvester" +}; + +/* +================== +Cmd_CallVote_f +================== +*/ +void Cmd_CallVote_f( gentity_t *ent ) { + char* c; + int i; + char arg1[MAX_STRING_TOKENS]; + char arg2[MAX_STRING_TOKENS]; + + if ( !g_allowVote.integer ) { + trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here.\n\"" ); + return; + } + + if ( level.voteTime ) { + trap_SendServerCommand( ent-g_entities, "print \"A vote is already in progress.\n\"" ); + return; + } + if ( ent->client->pers.voteCount >= MAX_VOTE_COUNT ) { + trap_SendServerCommand( ent-g_entities, "print \"You have called the maximum number of votes.\n\"" ); + return; + } + if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { + trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" ); + return; + } + + // make sure it is a valid command to vote on + trap_Argv( 1, arg1, sizeof( arg1 ) ); + trap_Argv( 2, arg2, sizeof( arg2 ) ); + + // check for command separators in arg2 + for( c = arg2; *c; ++c) { + switch(*c) { + case '\n': + case '\r': + case ';': + trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); + return; + break; + } + } + + if ( !Q_stricmp( arg1, "map_restart" ) ) { + } else if ( !Q_stricmp( arg1, "nextmap" ) ) { + } else if ( !Q_stricmp( arg1, "map" ) ) { + } else if ( !Q_stricmp( arg1, "g_gametype" ) ) { + } else if ( !Q_stricmp( arg1, "kick" ) ) { + } else if ( !Q_stricmp( arg1, "clientkick" ) ) { + } else if ( !Q_stricmp( arg1, "g_doWarmup" ) ) { + } else if ( !Q_stricmp( arg1, "timelimit" ) ) { + } else if ( !Q_stricmp( arg1, "fraglimit" ) ) { + } else { + trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); + trap_SendServerCommand( ent-g_entities, "print \"Vote commands are: map_restart, nextmap, map , g_gametype , kick , clientkick , g_doWarmup, timelimit