From eb73a11506d0cf83cdcf26aa7cc2e26c1f40b5bf Mon Sep 17 00:00:00 2001 From: dolcetriade Date: Tue, 24 Jan 2012 20:24:27 -0800 Subject: [PATCH] Upload the codebase --- main/glsl/blurX_fp.glsl | 159 + main/glsl/blurX_vp.glsl | 33 + main/glsl/blurY_fp.glsl | 159 + main/glsl/blurY_vp.glsl | 33 + main/glsl/cameraEffects_fp.glsl | 64 + main/glsl/cameraEffects_vp.glsl | 39 + main/glsl/contrast_fp.glsl | 64 + main/glsl/contrast_vp.glsl | 33 + main/glsl/debugShadowMap_fp.glsl | 61 + main/glsl/debugShadowMap_vp.glsl | 38 + main/glsl/deformVertexes_vp.glsl | 212 + main/glsl/depthToColor_fp.glsl | 57 + main/glsl/depthToColor_vp.glsl | 60 + main/glsl/dispersion_C_fp.glsl | 67 + main/glsl/dispersion_C_vp.glsl | 80 + main/glsl/fogGlobal_fp.glsl | 70 + main/glsl/fogGlobal_vp.glsl | 33 + main/glsl/fogQuake3_fp.glsl | 53 + main/glsl/fogQuake3_vp.glsl | 119 + main/glsl/forwardLighting_fp.glsl | 1107 ++ main/glsl/forwardLighting_vp.glsl | 154 + main/glsl/generic_fp.glsl | 72 + main/glsl/generic_vp.glsl | 111 + main/glsl/heatHaze_fp.glsl | 98 + main/glsl/heatHaze_vp.glsl | 93 + main/glsl/lightMapping_fp.glsl | 259 + main/glsl/lightMapping_vp.glsl | 114 + main/glsl/lightVolume_omni_fp.glsl | 151 + main/glsl/lightVolume_omni_vp.glsl | 33 + main/glsl/liquid_fp.glsl | 200 + main/glsl/liquid_vp.glsl | 65 + main/glsl/portal_fp.glsl | 48 + main/glsl/portal_vp.glsl | 44 + main/glsl/reflection_CB_fp.glsl | 72 + main/glsl/reflection_CB_vp.glsl | 118 + main/glsl/refraction_C_fp.glsl | 63 + main/glsl/refraction_C_vp.glsl | 80 + main/glsl/reliefMapping_fp.glsl | 79 + main/glsl/screen_fp.glsl | 41 + main/glsl/screen_vp.glsl | 39 + main/glsl/shadowFill_fp.glsl | 140 + main/glsl/shadowFill_vp.glsl | 93 + main/glsl/skybox_fp.glsl | 57 + main/glsl/skybox_vp.glsl | 40 + main/glsl/vertexAnimation_vp.glsl | 56 + main/glsl/vertexLighting_DBS_entity_fp.glsl | 269 + main/glsl/vertexLighting_DBS_entity_vp.glsl | 133 + main/glsl/vertexLighting_DBS_world_fp.glsl | 306 + main/glsl/vertexLighting_DBS_world_vp.glsl | 100 + main/glsl/vertexSkinning_vp.glsl | 115 + main/glsl/volumetricFog_fp.glsl | 121 + main/glsl/volumetricFog_vp.glsl | 33 + src/engine/OpenWolf.sln | 358 + src/engine/OpenWolf.vcxproj | 1231 ++ src/engine/OpenWolf.vcxproj.filters | 1013 + src/engine/asm/ftola.asm | 90 + src/engine/asm/ftola.c | 88 + src/engine/asm/matha.s | 54 + src/engine/asm/qasm-inline.h | 40 + src/engine/asm/qasm.h | 37 + src/engine/asm/snapvector.asm | 107 + src/engine/asm/snapvector.c | 87 + src/engine/asm/snd_mixa.s | 217 + src/engine/asm/vm_x86_64.asm | 76 + src/engine/botlib/aasfile.h | 291 + src/engine/botlib/be_aas.h | 253 + src/engine/botlib/be_aas_bsp.h | 103 + src/engine/botlib/be_aas_bspq3.c | 597 + src/engine/botlib/be_aas_cluster.c | 1761 ++ src/engine/botlib/be_aas_cluster.h | 47 + src/engine/botlib/be_aas_debug.c | 813 + src/engine/botlib/be_aas_debug.h | 87 + src/engine/botlib/be_aas_def.h | 309 + src/engine/botlib/be_aas_entity.c | 616 + src/engine/botlib/be_aas_entity.h | 86 + src/engine/botlib/be_aas_file.c | 750 + src/engine/botlib/be_aas_file.h | 56 + src/engine/botlib/be_aas_funcs.h | 62 + src/engine/botlib/be_aas_main.c | 555 + src/engine/botlib/be_aas_main.h | 85 + src/engine/botlib/be_aas_move.c | 1153 ++ src/engine/botlib/be_aas_move.h | 83 + src/engine/botlib/be_aas_optimize.c | 766 + src/engine/botlib/be_aas_optimize.h | 46 + src/engine/botlib/be_aas_reach.c | 5216 +++++ src/engine/botlib/be_aas_reach.h | 98 + src/engine/botlib/be_aas_route.c | 4270 ++++ src/engine/botlib/be_aas_route.h | 84 + src/engine/botlib/be_aas_routealt.c | 335 + src/engine/botlib/be_aas_routealt.h | 51 + src/engine/botlib/be_aas_routetable.c | 1270 ++ src/engine/botlib/be_aas_routetable.h | 176 + src/engine/botlib/be_aas_sample.c | 1683 ++ src/engine/botlib/be_aas_sample.h | 85 + src/engine/botlib/be_ai_char.c | 898 + src/engine/botlib/be_ai_char.h | 66 + src/engine/botlib/be_ai_chat.c | 3422 ++++ src/engine/botlib/be_ai_chat.h | 145 + src/engine/botlib/be_ai_gen.c | 171 + src/engine/botlib/be_ai_gen.h | 44 + src/engine/botlib/be_ai_goal.c | 1865 ++ src/engine/botlib/be_ai_goal.h | 174 + src/engine/botlib/be_ai_move.c | 4521 +++++ src/engine/botlib/be_ai_move.h | 155 + src/engine/botlib/be_ai_weap.c | 658 + src/engine/botlib/be_ai_weap.h | 122 + src/engine/botlib/be_ai_weight.c | 1230 ++ src/engine/botlib/be_ai_weight.h | 104 + src/engine/botlib/be_ea.c | 569 + src/engine/botlib/be_ea.h | 82 + src/engine/botlib/be_interface.c | 1023 + src/engine/botlib/be_interface.h | 99 + src/engine/botlib/botai.h | 116 + src/engine/botlib/botlib.def | 2 + src/engine/botlib/botlib.h | 616 + src/engine/botlib/botlib.vcxproj | 300 + src/engine/botlib/botlib.vcxproj.filters | 218 + src/engine/botlib/botlib_stub.c | 285 + src/engine/botlib/chars.h | 165 + src/engine/botlib/inv.h | 142 + src/engine/botlib/l_common.h | 40 + src/engine/botlib/l_crc.c | 170 + src/engine/botlib/l_crc.h | 50 + src/engine/botlib/l_libvar.c | 319 + src/engine/botlib/l_libvar.h | 83 + src/engine/botlib/l_log.c | 214 + src/engine/botlib/l_log.h | 66 + src/engine/botlib/l_memory.c | 503 + src/engine/botlib/l_memory.h | 97 + src/engine/botlib/l_precomp.c | 3888 ++++ src/engine/botlib/l_precomp.h | 196 + src/engine/botlib/l_script.c | 1703 ++ src/engine/botlib/l_script.h | 296 + src/engine/botlib/l_struct.c | 620 + src/engine/botlib/l_struct.h | 88 + src/engine/botlib/l_utils.h | 49 + src/engine/botlib/match.h | 138 + src/engine/botlib/syn.h | 52 + src/engine/client/cg_api.h | 487 + src/engine/client/cin_ogm.c | 1012 + src/engine/client/cl_avi.c | 659 + src/engine/client/cl_cgame.c | 1848 ++ src/engine/client/cl_cin.c | 1845 ++ src/engine/client/cl_console.c | 1169 ++ src/engine/client/cl_input.c | 1699 ++ src/engine/client/cl_irc.c | 2271 +++ src/engine/client/cl_keys.c | 1642 ++ src/engine/client/cl_main.c | 7122 +++++++ src/engine/client/cl_net_chan.c | 232 + src/engine/client/cl_parse.c | 1259 ++ src/engine/client/cl_scrn.c | 772 + src/engine/client/cl_ui.c | 1548 ++ src/engine/client/client.h | 904 + src/engine/client/keys.h | 71 + src/engine/client/libmumblelink.c | 185 + src/engine/client/libmumblelink.h | 35 + src/engine/client/snd_adpcm.c | 360 + src/engine/client/snd_api.h | 145 + src/engine/client/snd_codec.c | 207 + src/engine/client/snd_codec.h | 105 + src/engine/client/snd_codec_ogg.c | 472 + src/engine/client/snd_codec_wav.c | 277 + src/engine/client/snd_dma.c | 1589 ++ src/engine/client/snd_load.c | 450 + src/engine/client/snd_local.h | 246 + src/engine/client/snd_mem.c | 271 + src/engine/client/snd_mix.c | 635 + src/engine/client/snd_public.h | 95 + src/engine/client/snd_wavelet.c | 263 + src/engine/client/ui_api.h | 375 + src/engine/database/database.h | 149 + src/engine/database/db_main.c | 246 + src/engine/database/db_mysql.c | 532 + src/engine/null/null_client.c | 132 + src/engine/null/null_input.c | 58 + src/engine/null/null_snddma.c | 89 + src/engine/qcommon/cm_load.c | 1288 ++ src/engine/qcommon/cm_local.h | 313 + src/engine/qcommon/cm_patch.c | 1325 ++ src/engine/qcommon/cm_patch.h | 95 + src/engine/qcommon/cm_physics.cpp | 348 + src/engine/qcommon/cm_polylib.c | 828 + src/engine/qcommon/cm_polylib.h | 77 + src/engine/qcommon/cm_public.h | 109 + src/engine/qcommon/cm_test.c | 618 + src/engine/qcommon/cm_trace.c | 2798 +++ src/engine/qcommon/cm_trisoup.c | 1056 + src/engine/qcommon/cmd.c | 1903 ++ src/engine/qcommon/common.c | 4394 +++++ src/engine/qcommon/crypto.c | 85 + src/engine/qcommon/crypto.h | 47 + src/engine/qcommon/cvar.c | 1335 ++ src/engine/qcommon/dl_local.h | 64 + src/engine/qcommon/dl_main.c | 397 + src/engine/qcommon/dl_main_curl.c | 226 + src/engine/qcommon/dl_main_stubs.c | 68 + src/engine/qcommon/dl_public.h | 59 + src/engine/qcommon/files.c | 4489 +++++ src/engine/qcommon/htable.c | 653 + src/engine/qcommon/htable.h | 201 + src/engine/qcommon/huffman.c | 580 + src/engine/qcommon/ioapi.c | 235 + src/engine/qcommon/ioapi.h | 204 + src/engine/qcommon/md4.c | 229 + src/engine/qcommon/md4.h | 44 + src/engine/qcommon/md5.c | 310 + src/engine/qcommon/msg.c | 2731 +++ src/engine/qcommon/net_chan.c | 752 + src/engine/qcommon/net_http.c | 554 + src/engine/qcommon/net_ip.c | 1697 ++ src/engine/qcommon/parse.c | 3747 ++++ src/engine/qcommon/q_math.c | 3683 ++++ src/engine/qcommon/q_platform.h | 457 + src/engine/qcommon/q_shared.c | 2471 +++ src/engine/qcommon/q_shared.h | 2482 +++ src/engine/qcommon/qcommon.h | 1540 ++ src/engine/qcommon/qfiles.h | 1174 ++ src/engine/qcommon/surfaceflags.h | 113 + src/engine/qcommon/unzip.c | 2127 ++ src/engine/qcommon/unzip.h | 437 + src/engine/qcommon/vm.c | 973 + src/engine/qcommon/vm_interpreted.c | 921 + src/engine/qcommon/vm_llvm.cpp | 155 + src/engine/qcommon/vm_llvm.h | 36 + src/engine/qcommon/vm_local.h | 217 + src/engine/qcommon/vm_x86.c | 1832 ++ src/engine/renderer/anorms256.h | 291 + src/engine/renderer/renderer.sln | 20 + src/engine/renderer/renderer.vcproj | 944 + src/engine/renderer/renderer.vcxproj | 277 + src/engine/renderer/renderer.vcxproj.filters | 331 + src/engine/renderer/rendererGL.def | 2 + src/engine/renderer/tr_animation.c | 969 + src/engine/renderer/tr_animation_mdm.c | 2120 ++ src/engine/renderer/tr_animation_mds.c | 1760 ++ src/engine/renderer/tr_backend.c | 1746 ++ src/engine/renderer/tr_bsp.c | 3098 +++ src/engine/renderer/tr_cmds.c | 829 + src/engine/renderer/tr_cmesh.c | 492 + src/engine/renderer/tr_curve.c | 733 + src/engine/renderer/tr_decals.c | 1038 + src/engine/renderer/tr_flares.c | 589 + src/engine/renderer/tr_font.c | 620 + src/engine/renderer/tr_image.c | 3655 ++++ src/engine/renderer/tr_init.c | 1653 ++ src/engine/renderer/tr_light.c | 610 + src/engine/renderer/tr_local.h | 2555 +++ src/engine/renderer/tr_main.c | 2069 ++ src/engine/renderer/tr_marks.c | 1173 ++ src/engine/renderer/tr_mesh.c | 501 + src/engine/renderer/tr_model.c | 2854 +++ src/engine/renderer/tr_model_md5.c | 532 + src/engine/renderer/tr_noise.c | 108 + src/engine/renderer/tr_public.h | 287 + src/engine/renderer/tr_scene.c | 763 + src/engine/renderer/tr_shade.c | 1960 ++ src/engine/renderer/tr_shade_calc.c | 1596 ++ src/engine/renderer/tr_shader.c | 5637 ++++++ src/engine/renderer/tr_shadows.c | 385 + src/engine/renderer/tr_skin.c | 457 + src/engine/renderer/tr_sky.c | 1160 ++ src/engine/renderer/tr_surface.c | 1971 ++ src/engine/renderer/tr_types.h | 579 + src/engine/renderer/tr_world.c | 1213 ++ src/engine/rendererGL/gl_shader.cpp | 3683 ++++ src/engine/rendererGL/gl_shader.h | 2541 +++ src/engine/rendererGL/rendererD3D10.def | 2 + src/engine/rendererGL/rendererD3D10.vcxproj | 443 + .../rendererGL/rendererD3D10.vcxproj.filters | 376 + src/engine/rendererGL/rendererGL.def | 2 + src/engine/rendererGL/rendererGL.vcxproj | 453 + .../rendererGL/rendererGL.vcxproj.filters | 616 + src/engine/rendererGL/tr_animation.c | 1639 ++ src/engine/rendererGL/tr_animation_mdm.c | 2432 +++ src/engine/rendererGL/tr_backend.cpp | 11827 +++++++++++ src/engine/rendererGL/tr_backend_d3d10.cpp | 1246 ++ src/engine/rendererGL/tr_bsp.c | 9282 +++++++++ src/engine/rendererGL/tr_cmds.c | 831 + src/engine/rendererGL/tr_curve.c | 1012 + src/engine/rendererGL/tr_decals.c | 1018 + src/engine/rendererGL/tr_fbo.c | 1013 + src/engine/rendererGL/tr_flares.c | 567 + src/engine/rendererGL/tr_fog.c | 528 + src/engine/rendererGL/tr_font.c | 607 + src/engine/rendererGL/tr_image.c | 3780 ++++ src/engine/rendererGL/tr_image.h | 49 + src/engine/rendererGL/tr_image_dds.c | 1190 ++ src/engine/rendererGL/tr_image_exr.cpp | 104 + src/engine/rendererGL/tr_image_jpg.c | 450 + src/engine/rendererGL/tr_image_png.c | 313 + src/engine/rendererGL/tr_image_tga.c | 297 + src/engine/rendererGL/tr_image_webp.c | 73 + src/engine/rendererGL/tr_init.cpp | 2404 +++ src/engine/rendererGL/tr_light.c | 2042 ++ src/engine/rendererGL/tr_local.h | 5164 +++++ src/engine/rendererGL/tr_main.c | 3495 ++++ src/engine/rendererGL/tr_marks.c | 516 + src/engine/rendererGL/tr_mesh.c | 476 + src/engine/rendererGL/tr_model.c | 952 + src/engine/rendererGL/tr_model_md3.c | 517 + src/engine/rendererGL/tr_model_md5.c | 732 + src/engine/rendererGL/tr_model_mdc.c | 881 + src/engine/rendererGL/tr_model_mdm.c | 956 + src/engine/rendererGL/tr_model_psk.c | 943 + src/engine/rendererGL/tr_model_skel.c | 671 + src/engine/rendererGL/tr_model_skel.h | 34 + src/engine/rendererGL/tr_noise.c | 96 + src/engine/rendererGL/tr_scene.c | 791 + src/engine/rendererGL/tr_shade.cpp | 5544 ++++++ src/engine/rendererGL/tr_shade_calc.c | 1280 ++ src/engine/rendererGL/tr_shade_d3d10.cpp | 704 + src/engine/rendererGL/tr_shader.c | 6647 +++++++ src/engine/rendererGL/tr_shadows.c | 85 + src/engine/rendererGL/tr_skin.c | 463 + src/engine/rendererGL/tr_sky.cpp | 997 + src/engine/rendererGL/tr_surface.c | 2423 +++ src/engine/rendererGL/tr_vbo.c | 798 + src/engine/rendererGL/tr_world.cpp | 2534 +++ src/engine/server/g_api.h | 372 + src/engine/server/server.h | 627 + src/engine/server/sv_bot.c | 889 + src/engine/server/sv_ccmds.c | 1387 ++ src/engine/server/sv_client.c | 2442 +++ src/engine/server/sv_game.c | 1385 ++ src/engine/server/sv_init.c | 1233 ++ src/engine/server/sv_main.c | 1496 ++ src/engine/server/sv_net_chan.c | 311 + src/engine/server/sv_snapshot.c | 1136 ++ src/engine/server/sv_world.c | 827 + src/engine/snd_openal/qal.c | 361 + src/engine/snd_openal/qal.h | 236 + src/engine/snd_openal/snd_al_buffers.c | 331 + src/engine/snd_openal/snd_al_local.h | 220 + src/engine/snd_openal/snd_al_main.c | 608 + src/engine/snd_openal/snd_al_music.c | 222 + src/engine/snd_openal/snd_al_sources.c | 506 + src/engine/snd_openal/snd_al_stream.c | 166 + src/engine/snd_openal/snd_openal.vcxproj | 187 + .../snd_openal/snd_openal.vcxproj.filters | 45 + src/engine/sys/OWApplication.h | 67 + src/engine/sys/OWApplication.m | 479 + src/engine/sys/OWDownloader.h | 32 + src/engine/sys/OWDownloader.m | 277 + src/engine/sys/OWScreenView.h | 86 + src/engine/sys/OWScreenView.m | 415 + src/engine/sys/background.bmp | Bin 0 -> 197688 bytes src/engine/sys/clear.bmp | Bin 0 -> 5174 bytes src/engine/sys/con_curses.c | 602 + src/engine/sys/con_log.c | 141 + src/engine/sys/con_passive.c | 80 + src/engine/sys/con_tty.c | 431 + src/engine/sys/con_win32.c | 391 + src/engine/sys/openwolf.ico | Bin 0 -> 270398 bytes src/engine/sys/resource.h | 55 + src/engine/sys/sdl_gamma.c | 139 + src/engine/sys/sdl_glimp.c | 1970 ++ src/engine/sys/sdl_icon.h | 131 + src/engine/sys/sdl_input.c | 1374 ++ src/engine/sys/sdl_snd.c | 346 + src/engine/sys/sdl_vid.c | 540 + src/engine/sys/sys_loadlib.h | 61 + src/engine/sys/sys_local.h | 77 + src/engine/sys/sys_main.c | 898 + src/engine/sys/sys_osx.m | 143 + src/engine/sys/sys_sysinfo.c | 205 + src/engine/sys/sys_unix.c | 959 + src/engine/sys/sys_win32.c | 1134 ++ src/engine/sys/sys_win32.h | 64 + src/engine/sys/sys_win32_con.c | 896 + src/engine/sys/sys_win32_eh.cpp | 267 + src/engine/sys/winquake.aps | Bin 0 -> 327596 bytes src/engine/sys/winquake.rc | 71 + src/engine/sys/winquake.res | Bin 0 -> 270572 bytes src/gamelogic/etmain/src/ui/keycodes.h | 305 + src/gamelogic/gpp/src/cgame/cg_api.c | 1082 + src/libs/openexr/AUTHORS | 21 + src/libs/openexr/COPYING | 34 + src/libs/openexr/LICENSE | 34 + src/libs/openexr/README | 60 + src/libs/openexr/eLut.h | 71 + src/libs/openexr/half.cpp | 317 + src/libs/openexr/half.h | 776 + src/libs/openexr/halfLimits.h | 102 + src/libs/openexr/toFloat.h | 16391 ++++++++++++++++ src/libs/zlib/ChangeLog | 855 + src/libs/zlib/FAQ | 339 + src/libs/zlib/INDEX | 51 + src/libs/zlib/Makefile | 154 + src/libs/zlib/README | 125 + src/libs/zlib/adler32.c | 149 + src/libs/zlib/algorithm.txt | 209 + src/libs/zlib/compress.c | 79 + src/libs/zlib/crc32.c | 423 + src/libs/zlib/crc32.h | 441 + src/libs/zlib/deflate.c | 1736 ++ src/libs/zlib/deflate.h | 331 + src/libs/zlib/gzio.c | 1026 + src/libs/zlib/infback.c | 623 + src/libs/zlib/inffast.c | 318 + src/libs/zlib/inffast.h | 11 + src/libs/zlib/inffixed.h | 94 + src/libs/zlib/inflate.c | 1368 ++ src/libs/zlib/inflate.h | 115 + src/libs/zlib/inftrees.c | 329 + src/libs/zlib/inftrees.h | 55 + src/libs/zlib/ioapi.c | 174 + src/libs/zlib/ioapi.h | 69 + src/libs/zlib/trees.c | 1219 ++ src/libs/zlib/trees.h | 128 + src/libs/zlib/uncompr.c | 61 + src/libs/zlib/zconf.h | 340 + src/libs/zlib/zlib.h | 1357 ++ src/libs/zlib/zutil.c | 318 + src/libs/zlib/zutil.h | 269 + 414 files changed, 336193 insertions(+) create mode 100644 main/glsl/blurX_fp.glsl create mode 100644 main/glsl/blurX_vp.glsl create mode 100644 main/glsl/blurY_fp.glsl create mode 100644 main/glsl/blurY_vp.glsl create mode 100644 main/glsl/cameraEffects_fp.glsl create mode 100644 main/glsl/cameraEffects_vp.glsl create mode 100644 main/glsl/contrast_fp.glsl create mode 100644 main/glsl/contrast_vp.glsl create mode 100644 main/glsl/debugShadowMap_fp.glsl create mode 100644 main/glsl/debugShadowMap_vp.glsl create mode 100644 main/glsl/deformVertexes_vp.glsl create mode 100644 main/glsl/depthToColor_fp.glsl create mode 100644 main/glsl/depthToColor_vp.glsl create mode 100644 main/glsl/dispersion_C_fp.glsl create mode 100644 main/glsl/dispersion_C_vp.glsl create mode 100644 main/glsl/fogGlobal_fp.glsl create mode 100644 main/glsl/fogGlobal_vp.glsl create mode 100644 main/glsl/fogQuake3_fp.glsl create mode 100644 main/glsl/fogQuake3_vp.glsl create mode 100644 main/glsl/forwardLighting_fp.glsl create mode 100644 main/glsl/forwardLighting_vp.glsl create mode 100644 main/glsl/generic_fp.glsl create mode 100644 main/glsl/generic_vp.glsl create mode 100644 main/glsl/heatHaze_fp.glsl create mode 100644 main/glsl/heatHaze_vp.glsl create mode 100644 main/glsl/lightMapping_fp.glsl create mode 100644 main/glsl/lightMapping_vp.glsl create mode 100644 main/glsl/lightVolume_omni_fp.glsl create mode 100644 main/glsl/lightVolume_omni_vp.glsl create mode 100644 main/glsl/liquid_fp.glsl create mode 100644 main/glsl/liquid_vp.glsl create mode 100644 main/glsl/portal_fp.glsl create mode 100644 main/glsl/portal_vp.glsl create mode 100644 main/glsl/reflection_CB_fp.glsl create mode 100644 main/glsl/reflection_CB_vp.glsl create mode 100644 main/glsl/refraction_C_fp.glsl create mode 100644 main/glsl/refraction_C_vp.glsl create mode 100644 main/glsl/reliefMapping_fp.glsl create mode 100644 main/glsl/screen_fp.glsl create mode 100644 main/glsl/screen_vp.glsl create mode 100644 main/glsl/shadowFill_fp.glsl create mode 100644 main/glsl/shadowFill_vp.glsl create mode 100644 main/glsl/skybox_fp.glsl create mode 100644 main/glsl/skybox_vp.glsl create mode 100644 main/glsl/vertexAnimation_vp.glsl create mode 100644 main/glsl/vertexLighting_DBS_entity_fp.glsl create mode 100644 main/glsl/vertexLighting_DBS_entity_vp.glsl create mode 100644 main/glsl/vertexLighting_DBS_world_fp.glsl create mode 100644 main/glsl/vertexLighting_DBS_world_vp.glsl create mode 100644 main/glsl/vertexSkinning_vp.glsl create mode 100644 main/glsl/volumetricFog_fp.glsl create mode 100644 main/glsl/volumetricFog_vp.glsl create mode 100644 src/engine/OpenWolf.sln create mode 100644 src/engine/OpenWolf.vcxproj create mode 100644 src/engine/OpenWolf.vcxproj.filters create mode 100644 src/engine/asm/ftola.asm create mode 100644 src/engine/asm/ftola.c create mode 100644 src/engine/asm/matha.s create mode 100644 src/engine/asm/qasm-inline.h create mode 100644 src/engine/asm/qasm.h create mode 100644 src/engine/asm/snapvector.asm create mode 100644 src/engine/asm/snapvector.c create mode 100644 src/engine/asm/snd_mixa.s create mode 100644 src/engine/asm/vm_x86_64.asm create mode 100644 src/engine/botlib/aasfile.h create mode 100644 src/engine/botlib/be_aas.h create mode 100644 src/engine/botlib/be_aas_bsp.h create mode 100644 src/engine/botlib/be_aas_bspq3.c create mode 100644 src/engine/botlib/be_aas_cluster.c create mode 100644 src/engine/botlib/be_aas_cluster.h create mode 100644 src/engine/botlib/be_aas_debug.c create mode 100644 src/engine/botlib/be_aas_debug.h create mode 100644 src/engine/botlib/be_aas_def.h create mode 100644 src/engine/botlib/be_aas_entity.c create mode 100644 src/engine/botlib/be_aas_entity.h create mode 100644 src/engine/botlib/be_aas_file.c create mode 100644 src/engine/botlib/be_aas_file.h create mode 100644 src/engine/botlib/be_aas_funcs.h create mode 100644 src/engine/botlib/be_aas_main.c create mode 100644 src/engine/botlib/be_aas_main.h create mode 100644 src/engine/botlib/be_aas_move.c create mode 100644 src/engine/botlib/be_aas_move.h create mode 100644 src/engine/botlib/be_aas_optimize.c create mode 100644 src/engine/botlib/be_aas_optimize.h create mode 100644 src/engine/botlib/be_aas_reach.c create mode 100644 src/engine/botlib/be_aas_reach.h create mode 100644 src/engine/botlib/be_aas_route.c create mode 100644 src/engine/botlib/be_aas_route.h create mode 100644 src/engine/botlib/be_aas_routealt.c create mode 100644 src/engine/botlib/be_aas_routealt.h create mode 100644 src/engine/botlib/be_aas_routetable.c create mode 100644 src/engine/botlib/be_aas_routetable.h create mode 100644 src/engine/botlib/be_aas_sample.c create mode 100644 src/engine/botlib/be_aas_sample.h create mode 100644 src/engine/botlib/be_ai_char.c create mode 100644 src/engine/botlib/be_ai_char.h create mode 100644 src/engine/botlib/be_ai_chat.c create mode 100644 src/engine/botlib/be_ai_chat.h create mode 100644 src/engine/botlib/be_ai_gen.c create mode 100644 src/engine/botlib/be_ai_gen.h create mode 100644 src/engine/botlib/be_ai_goal.c create mode 100644 src/engine/botlib/be_ai_goal.h create mode 100644 src/engine/botlib/be_ai_move.c create mode 100644 src/engine/botlib/be_ai_move.h create mode 100644 src/engine/botlib/be_ai_weap.c create mode 100644 src/engine/botlib/be_ai_weap.h create mode 100644 src/engine/botlib/be_ai_weight.c create mode 100644 src/engine/botlib/be_ai_weight.h create mode 100644 src/engine/botlib/be_ea.c create mode 100644 src/engine/botlib/be_ea.h create mode 100644 src/engine/botlib/be_interface.c create mode 100644 src/engine/botlib/be_interface.h create mode 100644 src/engine/botlib/botai.h create mode 100644 src/engine/botlib/botlib.def create mode 100644 src/engine/botlib/botlib.h create mode 100644 src/engine/botlib/botlib.vcxproj create mode 100644 src/engine/botlib/botlib.vcxproj.filters create mode 100644 src/engine/botlib/botlib_stub.c create mode 100644 src/engine/botlib/chars.h create mode 100644 src/engine/botlib/inv.h create mode 100644 src/engine/botlib/l_common.h create mode 100644 src/engine/botlib/l_crc.c create mode 100644 src/engine/botlib/l_crc.h create mode 100644 src/engine/botlib/l_libvar.c create mode 100644 src/engine/botlib/l_libvar.h create mode 100644 src/engine/botlib/l_log.c create mode 100644 src/engine/botlib/l_log.h create mode 100644 src/engine/botlib/l_memory.c create mode 100644 src/engine/botlib/l_memory.h create mode 100644 src/engine/botlib/l_precomp.c create mode 100644 src/engine/botlib/l_precomp.h create mode 100644 src/engine/botlib/l_script.c create mode 100644 src/engine/botlib/l_script.h create mode 100644 src/engine/botlib/l_struct.c create mode 100644 src/engine/botlib/l_struct.h create mode 100644 src/engine/botlib/l_utils.h create mode 100644 src/engine/botlib/match.h create mode 100644 src/engine/botlib/syn.h create mode 100644 src/engine/client/cg_api.h create mode 100644 src/engine/client/cin_ogm.c create mode 100644 src/engine/client/cl_avi.c create mode 100644 src/engine/client/cl_cgame.c create mode 100644 src/engine/client/cl_cin.c create mode 100644 src/engine/client/cl_console.c create mode 100644 src/engine/client/cl_input.c create mode 100644 src/engine/client/cl_irc.c create mode 100644 src/engine/client/cl_keys.c create mode 100644 src/engine/client/cl_main.c create mode 100644 src/engine/client/cl_net_chan.c create mode 100644 src/engine/client/cl_parse.c create mode 100644 src/engine/client/cl_scrn.c create mode 100644 src/engine/client/cl_ui.c create mode 100644 src/engine/client/client.h create mode 100644 src/engine/client/keys.h create mode 100644 src/engine/client/libmumblelink.c create mode 100644 src/engine/client/libmumblelink.h create mode 100644 src/engine/client/snd_adpcm.c create mode 100644 src/engine/client/snd_api.h create mode 100644 src/engine/client/snd_codec.c create mode 100644 src/engine/client/snd_codec.h create mode 100644 src/engine/client/snd_codec_ogg.c create mode 100644 src/engine/client/snd_codec_wav.c create mode 100644 src/engine/client/snd_dma.c create mode 100644 src/engine/client/snd_load.c create mode 100644 src/engine/client/snd_local.h create mode 100644 src/engine/client/snd_mem.c create mode 100644 src/engine/client/snd_mix.c create mode 100644 src/engine/client/snd_public.h create mode 100644 src/engine/client/snd_wavelet.c create mode 100644 src/engine/client/ui_api.h create mode 100644 src/engine/database/database.h create mode 100644 src/engine/database/db_main.c create mode 100644 src/engine/database/db_mysql.c create mode 100644 src/engine/null/null_client.c create mode 100644 src/engine/null/null_input.c create mode 100644 src/engine/null/null_snddma.c create mode 100644 src/engine/qcommon/cm_load.c create mode 100644 src/engine/qcommon/cm_local.h create mode 100644 src/engine/qcommon/cm_patch.c create mode 100644 src/engine/qcommon/cm_patch.h create mode 100644 src/engine/qcommon/cm_physics.cpp create mode 100644 src/engine/qcommon/cm_polylib.c create mode 100644 src/engine/qcommon/cm_polylib.h create mode 100644 src/engine/qcommon/cm_public.h create mode 100644 src/engine/qcommon/cm_test.c create mode 100644 src/engine/qcommon/cm_trace.c create mode 100644 src/engine/qcommon/cm_trisoup.c create mode 100644 src/engine/qcommon/cmd.c create mode 100644 src/engine/qcommon/common.c create mode 100644 src/engine/qcommon/crypto.c create mode 100644 src/engine/qcommon/crypto.h create mode 100644 src/engine/qcommon/cvar.c create mode 100644 src/engine/qcommon/dl_local.h create mode 100644 src/engine/qcommon/dl_main.c create mode 100644 src/engine/qcommon/dl_main_curl.c create mode 100644 src/engine/qcommon/dl_main_stubs.c create mode 100644 src/engine/qcommon/dl_public.h create mode 100644 src/engine/qcommon/files.c create mode 100644 src/engine/qcommon/htable.c create mode 100644 src/engine/qcommon/htable.h create mode 100644 src/engine/qcommon/huffman.c create mode 100644 src/engine/qcommon/ioapi.c create mode 100644 src/engine/qcommon/ioapi.h create mode 100644 src/engine/qcommon/md4.c create mode 100644 src/engine/qcommon/md4.h create mode 100644 src/engine/qcommon/md5.c create mode 100644 src/engine/qcommon/msg.c create mode 100644 src/engine/qcommon/net_chan.c create mode 100644 src/engine/qcommon/net_http.c create mode 100644 src/engine/qcommon/net_ip.c create mode 100644 src/engine/qcommon/parse.c create mode 100644 src/engine/qcommon/q_math.c create mode 100644 src/engine/qcommon/q_platform.h create mode 100644 src/engine/qcommon/q_shared.c create mode 100644 src/engine/qcommon/q_shared.h create mode 100644 src/engine/qcommon/qcommon.h create mode 100644 src/engine/qcommon/qfiles.h create mode 100644 src/engine/qcommon/surfaceflags.h create mode 100644 src/engine/qcommon/unzip.c create mode 100644 src/engine/qcommon/unzip.h create mode 100644 src/engine/qcommon/vm.c create mode 100644 src/engine/qcommon/vm_interpreted.c create mode 100644 src/engine/qcommon/vm_llvm.cpp create mode 100644 src/engine/qcommon/vm_llvm.h create mode 100644 src/engine/qcommon/vm_local.h create mode 100644 src/engine/qcommon/vm_x86.c create mode 100644 src/engine/renderer/anorms256.h create mode 100644 src/engine/renderer/renderer.sln create mode 100644 src/engine/renderer/renderer.vcproj create mode 100644 src/engine/renderer/renderer.vcxproj create mode 100644 src/engine/renderer/renderer.vcxproj.filters create mode 100644 src/engine/renderer/rendererGL.def create mode 100644 src/engine/renderer/tr_animation.c create mode 100644 src/engine/renderer/tr_animation_mdm.c create mode 100644 src/engine/renderer/tr_animation_mds.c create mode 100644 src/engine/renderer/tr_backend.c create mode 100644 src/engine/renderer/tr_bsp.c create mode 100644 src/engine/renderer/tr_cmds.c create mode 100644 src/engine/renderer/tr_cmesh.c create mode 100644 src/engine/renderer/tr_curve.c create mode 100644 src/engine/renderer/tr_decals.c create mode 100644 src/engine/renderer/tr_flares.c create mode 100644 src/engine/renderer/tr_font.c create mode 100644 src/engine/renderer/tr_image.c create mode 100644 src/engine/renderer/tr_init.c create mode 100644 src/engine/renderer/tr_light.c create mode 100644 src/engine/renderer/tr_local.h create mode 100644 src/engine/renderer/tr_main.c create mode 100644 src/engine/renderer/tr_marks.c create mode 100644 src/engine/renderer/tr_mesh.c create mode 100644 src/engine/renderer/tr_model.c create mode 100644 src/engine/renderer/tr_model_md5.c create mode 100644 src/engine/renderer/tr_noise.c create mode 100644 src/engine/renderer/tr_public.h create mode 100644 src/engine/renderer/tr_scene.c create mode 100644 src/engine/renderer/tr_shade.c create mode 100644 src/engine/renderer/tr_shade_calc.c create mode 100644 src/engine/renderer/tr_shader.c create mode 100644 src/engine/renderer/tr_shadows.c create mode 100644 src/engine/renderer/tr_skin.c create mode 100644 src/engine/renderer/tr_sky.c create mode 100644 src/engine/renderer/tr_surface.c create mode 100644 src/engine/renderer/tr_types.h create mode 100644 src/engine/renderer/tr_world.c create mode 100644 src/engine/rendererGL/gl_shader.cpp create mode 100644 src/engine/rendererGL/gl_shader.h create mode 100644 src/engine/rendererGL/rendererD3D10.def create mode 100644 src/engine/rendererGL/rendererD3D10.vcxproj create mode 100644 src/engine/rendererGL/rendererD3D10.vcxproj.filters create mode 100644 src/engine/rendererGL/rendererGL.def create mode 100644 src/engine/rendererGL/rendererGL.vcxproj create mode 100644 src/engine/rendererGL/rendererGL.vcxproj.filters create mode 100644 src/engine/rendererGL/tr_animation.c create mode 100644 src/engine/rendererGL/tr_animation_mdm.c create mode 100644 src/engine/rendererGL/tr_backend.cpp create mode 100644 src/engine/rendererGL/tr_backend_d3d10.cpp create mode 100644 src/engine/rendererGL/tr_bsp.c create mode 100644 src/engine/rendererGL/tr_cmds.c create mode 100644 src/engine/rendererGL/tr_curve.c create mode 100644 src/engine/rendererGL/tr_decals.c create mode 100644 src/engine/rendererGL/tr_fbo.c create mode 100644 src/engine/rendererGL/tr_flares.c create mode 100644 src/engine/rendererGL/tr_fog.c create mode 100644 src/engine/rendererGL/tr_font.c create mode 100644 src/engine/rendererGL/tr_image.c create mode 100644 src/engine/rendererGL/tr_image.h create mode 100644 src/engine/rendererGL/tr_image_dds.c create mode 100644 src/engine/rendererGL/tr_image_exr.cpp create mode 100644 src/engine/rendererGL/tr_image_jpg.c create mode 100644 src/engine/rendererGL/tr_image_png.c create mode 100644 src/engine/rendererGL/tr_image_tga.c create mode 100644 src/engine/rendererGL/tr_image_webp.c create mode 100644 src/engine/rendererGL/tr_init.cpp create mode 100644 src/engine/rendererGL/tr_light.c create mode 100644 src/engine/rendererGL/tr_local.h create mode 100644 src/engine/rendererGL/tr_main.c create mode 100644 src/engine/rendererGL/tr_marks.c create mode 100644 src/engine/rendererGL/tr_mesh.c create mode 100644 src/engine/rendererGL/tr_model.c create mode 100644 src/engine/rendererGL/tr_model_md3.c create mode 100644 src/engine/rendererGL/tr_model_md5.c create mode 100644 src/engine/rendererGL/tr_model_mdc.c create mode 100644 src/engine/rendererGL/tr_model_mdm.c create mode 100644 src/engine/rendererGL/tr_model_psk.c create mode 100644 src/engine/rendererGL/tr_model_skel.c create mode 100644 src/engine/rendererGL/tr_model_skel.h create mode 100644 src/engine/rendererGL/tr_noise.c create mode 100644 src/engine/rendererGL/tr_scene.c create mode 100644 src/engine/rendererGL/tr_shade.cpp create mode 100644 src/engine/rendererGL/tr_shade_calc.c create mode 100644 src/engine/rendererGL/tr_shade_d3d10.cpp create mode 100644 src/engine/rendererGL/tr_shader.c create mode 100644 src/engine/rendererGL/tr_shadows.c create mode 100644 src/engine/rendererGL/tr_skin.c create mode 100644 src/engine/rendererGL/tr_sky.cpp create mode 100644 src/engine/rendererGL/tr_surface.c create mode 100644 src/engine/rendererGL/tr_vbo.c create mode 100644 src/engine/rendererGL/tr_world.cpp create mode 100644 src/engine/server/g_api.h create mode 100644 src/engine/server/server.h create mode 100644 src/engine/server/sv_bot.c create mode 100644 src/engine/server/sv_ccmds.c create mode 100644 src/engine/server/sv_client.c create mode 100644 src/engine/server/sv_game.c create mode 100644 src/engine/server/sv_init.c create mode 100644 src/engine/server/sv_main.c create mode 100644 src/engine/server/sv_net_chan.c create mode 100644 src/engine/server/sv_snapshot.c create mode 100644 src/engine/server/sv_world.c create mode 100644 src/engine/snd_openal/qal.c create mode 100644 src/engine/snd_openal/qal.h create mode 100644 src/engine/snd_openal/snd_al_buffers.c create mode 100644 src/engine/snd_openal/snd_al_local.h create mode 100644 src/engine/snd_openal/snd_al_main.c create mode 100644 src/engine/snd_openal/snd_al_music.c create mode 100644 src/engine/snd_openal/snd_al_sources.c create mode 100644 src/engine/snd_openal/snd_al_stream.c create mode 100644 src/engine/snd_openal/snd_openal.vcxproj create mode 100644 src/engine/snd_openal/snd_openal.vcxproj.filters create mode 100644 src/engine/sys/OWApplication.h create mode 100644 src/engine/sys/OWApplication.m create mode 100644 src/engine/sys/OWDownloader.h create mode 100644 src/engine/sys/OWDownloader.m create mode 100644 src/engine/sys/OWScreenView.h create mode 100644 src/engine/sys/OWScreenView.m create mode 100644 src/engine/sys/background.bmp create mode 100644 src/engine/sys/clear.bmp create mode 100644 src/engine/sys/con_curses.c create mode 100644 src/engine/sys/con_log.c create mode 100644 src/engine/sys/con_passive.c create mode 100644 src/engine/sys/con_tty.c create mode 100644 src/engine/sys/con_win32.c create mode 100644 src/engine/sys/openwolf.ico create mode 100644 src/engine/sys/resource.h create mode 100644 src/engine/sys/sdl_gamma.c create mode 100644 src/engine/sys/sdl_glimp.c create mode 100644 src/engine/sys/sdl_icon.h create mode 100644 src/engine/sys/sdl_input.c create mode 100644 src/engine/sys/sdl_snd.c create mode 100644 src/engine/sys/sdl_vid.c create mode 100644 src/engine/sys/sys_loadlib.h create mode 100644 src/engine/sys/sys_local.h create mode 100644 src/engine/sys/sys_main.c create mode 100644 src/engine/sys/sys_osx.m create mode 100644 src/engine/sys/sys_sysinfo.c create mode 100644 src/engine/sys/sys_unix.c create mode 100644 src/engine/sys/sys_win32.c create mode 100644 src/engine/sys/sys_win32.h create mode 100644 src/engine/sys/sys_win32_con.c create mode 100644 src/engine/sys/sys_win32_eh.cpp create mode 100644 src/engine/sys/winquake.aps create mode 100644 src/engine/sys/winquake.rc create mode 100644 src/engine/sys/winquake.res create mode 100644 src/gamelogic/etmain/src/ui/keycodes.h create mode 100644 src/gamelogic/gpp/src/cgame/cg_api.c create mode 100644 src/libs/openexr/AUTHORS create mode 100644 src/libs/openexr/COPYING create mode 100644 src/libs/openexr/LICENSE create mode 100644 src/libs/openexr/README create mode 100644 src/libs/openexr/eLut.h create mode 100644 src/libs/openexr/half.cpp create mode 100644 src/libs/openexr/half.h create mode 100644 src/libs/openexr/halfLimits.h create mode 100644 src/libs/openexr/toFloat.h create mode 100644 src/libs/zlib/ChangeLog create mode 100644 src/libs/zlib/FAQ create mode 100644 src/libs/zlib/INDEX create mode 100644 src/libs/zlib/Makefile create mode 100644 src/libs/zlib/README create mode 100644 src/libs/zlib/adler32.c create mode 100644 src/libs/zlib/algorithm.txt create mode 100644 src/libs/zlib/compress.c create mode 100644 src/libs/zlib/crc32.c create mode 100644 src/libs/zlib/crc32.h create mode 100644 src/libs/zlib/deflate.c create mode 100644 src/libs/zlib/deflate.h create mode 100644 src/libs/zlib/gzio.c create mode 100644 src/libs/zlib/infback.c create mode 100644 src/libs/zlib/inffast.c create mode 100644 src/libs/zlib/inffast.h create mode 100644 src/libs/zlib/inffixed.h create mode 100644 src/libs/zlib/inflate.c create mode 100644 src/libs/zlib/inflate.h create mode 100644 src/libs/zlib/inftrees.c create mode 100644 src/libs/zlib/inftrees.h create mode 100644 src/libs/zlib/ioapi.c create mode 100644 src/libs/zlib/ioapi.h create mode 100644 src/libs/zlib/trees.c create mode 100644 src/libs/zlib/trees.h create mode 100644 src/libs/zlib/uncompr.c create mode 100644 src/libs/zlib/zconf.h create mode 100644 src/libs/zlib/zlib.h create mode 100644 src/libs/zlib/zutil.c create mode 100644 src/libs/zlib/zutil.h diff --git a/main/glsl/blurX_fp.glsl b/main/glsl/blurX_fp.glsl new file mode 100644 index 0000000000..bf828c37d2 --- /dev/null +++ b/main/glsl/blurX_fp.glsl @@ -0,0 +1,159 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* blurX_fp.glsl */ + +uniform sampler2D u_ColorMap; +uniform float u_DeformMagnitude; + +void main() +{ + vec2 st = gl_FragCoord.st; + + // calculate the screen texcoord in the 0.0 to 1.0 range + st *= r_FBufScale; + + // multiply with 4 because the FBO is only 1/4th of the screen resolution + st *= vec2(4.0, 4.0); + + // scale by the screen non-power-of-two-adjust + st *= r_NPOTScale; + +#if 1 + // set so a magnitude of 1 is approximately 1 pixel with 640x480 + //vec2 deform = vec2(u_DeformMagnitude * 0.0016, u_DeformMagnitude * 0.00213333); + vec2 deform = u_DeformMagnitude * r_FBufScale; + + // these are the multipliers for the gaussian blur + float mu01 = 0.00261097; + float mu02 = 0.00522193; + float mu03 = 0.01044386; + float mu04 = 0.02088773; + float mu05 = 0.04177546; + float mu06 = 0.08355091; + float mu07 = 0.16710183; + float mu08 = 0.33420366; + float mu09 = 0.66840732; + + // fragment offsets for blur samples + vec2 offset01 = vec2(-8.0, 0.0); + vec2 offset02 = vec2(-7.0, 0.0); + vec2 offset03 = vec2(-6.0, 0.0); + vec2 offset04 = vec2(-5.0, 0.0); + vec2 offset05 = vec2(-4.0, 0.0); + vec2 offset06 = vec2(-3.0, 0.0); + vec2 offset07 = vec2(-2.0, 0.0); + vec2 offset08 = vec2(-1.0, 0.0); + vec2 offset09 = vec2( 1.0, 0.0); + vec2 offset10 = vec2( 2.0, 0.0); + vec2 offset11 = vec2( 3.0, 0.0); + vec2 offset12 = vec2( 4.0, 0.0); + vec2 offset13 = vec2( 5.0, 0.0); + vec2 offset14 = vec2( 6.0, 0.0); + vec2 offset15 = vec2( 7.0, 0.0); + vec2 offset16 = vec2( 8.0, 0.0); + + // calculate our offset texture coordinates + vec2 st01 = st + offset01 * deform; + vec2 st02 = st + offset02 * deform; + vec2 st03 = st + offset03 * deform; + vec2 st04 = st + offset04 * deform; + vec2 st05 = st + offset05 * deform; + vec2 st06 = st + offset06 * deform; + vec2 st07 = st + offset07 * deform; + vec2 st08 = st + offset08 * deform; + vec2 st09 = st + offset09 * deform; + vec2 st10 = st + offset10 * deform; + vec2 st11 = st + offset11 * deform; + vec2 st12 = st + offset12 * deform; + vec2 st13 = st + offset13 * deform; + vec2 st14 = st + offset14 * deform; + vec2 st15 = st + offset15 * deform; + vec2 st16 = st + offset16 * deform; + + // base color + vec4 c00 = texture2D(u_ColorMap, st); + + // sample the current render for each coordinate + vec4 c01 = texture2D(u_ColorMap, st01); + vec4 c02 = texture2D(u_ColorMap, st02); + vec4 c03 = texture2D(u_ColorMap, st03); + vec4 c04 = texture2D(u_ColorMap, st04); + vec4 c05 = texture2D(u_ColorMap, st05); + vec4 c06 = texture2D(u_ColorMap, st06); + vec4 c07 = texture2D(u_ColorMap, st07); + vec4 c08 = texture2D(u_ColorMap, st08); + vec4 c09 = texture2D(u_ColorMap, st09); + vec4 c10 = texture2D(u_ColorMap, st10); + vec4 c11 = texture2D(u_ColorMap, st11); + vec4 c12 = texture2D(u_ColorMap, st12); + vec4 c13 = texture2D(u_ColorMap, st13); + vec4 c14 = texture2D(u_ColorMap, st14); + vec4 c15 = texture2D(u_ColorMap, st15); + vec4 c16 = texture2D(u_ColorMap, st16); + + vec4 color = c01 * mu01; + color += c02 * mu02; + color += c03 * mu03; + color += c04 * mu04; + color += c05 * mu05; + color += c06 * mu06; + color += c07 * mu07; + color += c08 * mu08; + color += c00 * mu09; + color += c09 * mu08; + color += c10 * mu07; + color += c11 * mu06; + color += c12 * mu05; + color += c13 * mu04; + color += c14 * mu03; + color += c15 * mu02; + color += c16 * mu01; + + gl_FragColor = color; +#else + + #if 0 + float gaussFact[3] = float[3](1.0, 2.0, 1.0); + #elif 0 + float gaussFact[5] = float[5](1.0, 4.0, 6.0, 4.0, 1.0); + float gaussSum = 16.0; + #elif 1 + float gaussFact[7] = float[7](1.0, 6.0, 15.0, 20.0, 15.0, 6.0, 1.0); + float gaussSum = 64.0; + #endif + + // do a full gaussian blur + vec4 sumColors = vec4(0.0); + + int tap = 3; + for(int i = -tap; i < tap; i++) + { + float weight = gaussFact[i + 2]; + vec4 color = texture2D(u_ColorMap, st + vec2(i, 0) * u_DeformMagnitude * r_FBufScale) * weight; + + sumColors += color; + } + + gl_FragColor = sumColors / gaussSum; +#endif +} diff --git a/main/glsl/blurX_vp.glsl b/main/glsl/blurX_vp.glsl new file mode 100644 index 0000000000..d66b1e7427 --- /dev/null +++ b/main/glsl/blurX_vp.glsl @@ -0,0 +1,33 @@ +/* +=========================================================================== +Copyright (C) 2006-2008 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* blurX_vp.glsl */ + +attribute vec4 attr_Position; + +uniform mat4 u_ModelViewProjectionMatrix; + +void main() +{ + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * attr_Position; +} diff --git a/main/glsl/blurY_fp.glsl b/main/glsl/blurY_fp.glsl new file mode 100644 index 0000000000..c383c11927 --- /dev/null +++ b/main/glsl/blurY_fp.glsl @@ -0,0 +1,159 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* blurY_fp.glsl */ + +uniform sampler2D u_ColorMap; +uniform float u_DeformMagnitude; + +void main() +{ + vec2 st = gl_FragCoord.st; + + // calculate the screen texcoord in the 0.0 to 1.0 range + st *= r_FBufScale; + + // multiply with 4 because the FBO is only 1/4th of the screen resolution + st *= vec2(4.0, 4.0); + + // scale by the screen non-power-of-two-adjust + st *= r_NPOTScale; + +#if 1 + // set so a magnitude of 1 is approximately 1 pixel with 640x480 + //vec2 deform = vec2(u_DeformMagnitude * 0.0016, u_DeformMagnitude * 0.00213333); + vec2 deform = u_DeformMagnitude * r_FBufScale; + + // these are the multipliers for the gaussian blur + float mu01 = 0.00261097; + float mu02 = 0.00522193; + float mu03 = 0.01044386; + float mu04 = 0.02088773; + float mu05 = 0.04177546; + float mu06 = 0.08355091; + float mu07 = 0.16710183; + float mu08 = 0.33420366; + float mu09 = 0.66840732; + + // fragment offsets for blur samples + vec2 offset01 = vec2( 0.0, -8.0); + vec2 offset02 = vec2( 0.0, -7.0); + vec2 offset03 = vec2( 0.0, -6.0); + vec2 offset04 = vec2( 0.0, -5.0); + vec2 offset05 = vec2( 0.0, -4.0); + vec2 offset06 = vec2( 0.0, -3.0); + vec2 offset07 = vec2( 0.0, -2.0); + vec2 offset08 = vec2( 0.0, -1.0); + vec2 offset09 = vec2( 0.0, 1.0); + vec2 offset10 = vec2( 0.0, 2.0); + vec2 offset11 = vec2( 0.0, 3.0); + vec2 offset12 = vec2( 0.0, 4.0); + vec2 offset13 = vec2( 0.0, 5.0); + vec2 offset14 = vec2( 0.0, 6.0); + vec2 offset15 = vec2( 0.0, 7.0); + vec2 offset16 = vec2( 0.0, 8.0); + + // calculate our offset texture coordinates + vec2 st01 = st + offset01 * deform; + vec2 st02 = st + offset02 * deform; + vec2 st03 = st + offset03 * deform; + vec2 st04 = st + offset04 * deform; + vec2 st05 = st + offset05 * deform; + vec2 st06 = st + offset06 * deform; + vec2 st07 = st + offset07 * deform; + vec2 st08 = st + offset08 * deform; + vec2 st09 = st + offset09 * deform; + vec2 st10 = st + offset10 * deform; + vec2 st11 = st + offset11 * deform; + vec2 st12 = st + offset12 * deform; + vec2 st13 = st + offset13 * deform; + vec2 st14 = st + offset14 * deform; + vec2 st15 = st + offset15 * deform; + vec2 st16 = st + offset16 * deform; + + // base color + vec4 c00 = texture2D(u_ColorMap, st); + + // sample the current render for each coordinate + vec4 c01 = texture2D(u_ColorMap, st01); + vec4 c02 = texture2D(u_ColorMap, st02); + vec4 c03 = texture2D(u_ColorMap, st03); + vec4 c04 = texture2D(u_ColorMap, st04); + vec4 c05 = texture2D(u_ColorMap, st05); + vec4 c06 = texture2D(u_ColorMap, st06); + vec4 c07 = texture2D(u_ColorMap, st07); + vec4 c08 = texture2D(u_ColorMap, st08); + vec4 c09 = texture2D(u_ColorMap, st09); + vec4 c10 = texture2D(u_ColorMap, st10); + vec4 c11 = texture2D(u_ColorMap, st11); + vec4 c12 = texture2D(u_ColorMap, st12); + vec4 c13 = texture2D(u_ColorMap, st13); + vec4 c14 = texture2D(u_ColorMap, st14); + vec4 c15 = texture2D(u_ColorMap, st15); + vec4 c16 = texture2D(u_ColorMap, st16); + + vec4 color = c01 * mu01; + color += c02 * mu02; + color += c03 * mu03; + color += c04 * mu04; + color += c05 * mu05; + color += c06 * mu06; + color += c07 * mu07; + color += c08 * mu08; + color += c00 * mu09; + color += c09 * mu08; + color += c10 * mu07; + color += c11 * mu06; + color += c12 * mu05; + color += c13 * mu04; + color += c14 * mu03; + color += c15 * mu02; + color += c16 * mu01; + + gl_FragColor = color; +#else + + #if 0 + float gaussFact[3] = float[3](1.0, 2.0, 1.0); + #elif 0 + float gaussFact[5] = float[5](1.0, 4.0, 6.0, 4.0, 1.0); + float gaussSum = 16.0; + #elif 1 + float gaussFact[7] = float[7](1.0, 6.0, 15.0, 20.0, 15.0, 6.0, 1.0); + float gaussSum = 64.0; + #endif + + // do a full gaussian blur + vec4 sumColors = vec4(0.0); + + int tap = 3; + for(int i = -tap; i < tap; i++) + { + float weight = gaussFact[i + 2]; + vec4 color = texture2D(u_ColorMap, st + vec2(0, i) * u_DeformMagnitude * r_FBufScale) * weight; + + sumColors += color; + } + + gl_FragColor = sumColors / gaussSum; +#endif +} diff --git a/main/glsl/blurY_vp.glsl b/main/glsl/blurY_vp.glsl new file mode 100644 index 0000000000..91f33b8cf9 --- /dev/null +++ b/main/glsl/blurY_vp.glsl @@ -0,0 +1,33 @@ +/* +=========================================================================== +Copyright (C) 2006-2008 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* blurY_vp.glsl */ + +attribute vec4 attr_Position; + +uniform mat4 u_ModelViewProjectionMatrix; + +void main() +{ + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * attr_Position; +} diff --git a/main/glsl/cameraEffects_fp.glsl b/main/glsl/cameraEffects_fp.glsl new file mode 100644 index 0000000000..bcc6b0e0ae --- /dev/null +++ b/main/glsl/cameraEffects_fp.glsl @@ -0,0 +1,64 @@ +/* +=========================================================================== +Copyright (C) 2009-2010 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* cameraEffects_fp.glsl */ + +uniform sampler2D u_CurrentMap; +uniform sampler2D u_GrainMap; +uniform sampler2D u_VignetteMap; + +varying vec2 var_Tex; + +void main() +{ + // calculate the screen texcoord in the 0.0 to 1.0 range + vec2 stClamped = gl_FragCoord.st * r_FBufScale; + + // scale by the screen non-power-of-two-adjust + vec2 st = stClamped * r_NPOTScale; + + vec4 original = clamp(texture2D(u_CurrentMap, st), 0.0, 1.0); + + vec4 color = original; + + // calculate chromatic aberration +#if 0 + vec2 redOffset = vec2(0.5, 0.25); + vec2 greenOffset = vec2(0.0, 0.0); + vec2 blueOffset = vec2(-0.5, -0.25); + + color.r = texture2D(u_CurrentMap, st + redOffset * r_FBufScale).r; + color.g = texture2D(u_CurrentMap, st + greenOffset * r_FBufScale).g; + color.b = texture2D(u_CurrentMap, st + blueOffset * r_FBufScale).b; +#endif + + // blend the vignette + vec4 vignette = texture2D(u_VignetteMap, stClamped); + color.rgb *= vignette.rgb; + + // add grain + vec4 grain = texture2D(u_GrainMap, var_Tex); + color.rgb = (color.rgb + (grain.rgb * vec3(0.035, 0.065, 0.09))) + (color.rgb * (grain.rgb * vec3(0.035, 0.065, 0.09))); + //color.rgb = grain.rgb; + + gl_FragColor = color; +} diff --git a/main/glsl/cameraEffects_vp.glsl b/main/glsl/cameraEffects_vp.glsl new file mode 100644 index 0000000000..c0bfd556d2 --- /dev/null +++ b/main/glsl/cameraEffects_vp.glsl @@ -0,0 +1,39 @@ +/* +=========================================================================== +Copyright (C) 2009-2010 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* cameraEffects_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; + +uniform mat4 u_ModelViewProjectionMatrix; +uniform mat4 u_ColorTextureMatrix; + +varying vec2 var_Tex; + +void main() +{ + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * attr_Position; + + var_Tex = (u_ColorTextureMatrix * attr_TexCoord0).st; +} diff --git a/main/glsl/contrast_fp.glsl b/main/glsl/contrast_fp.glsl new file mode 100644 index 0000000000..f0a9db42ea --- /dev/null +++ b/main/glsl/contrast_fp.glsl @@ -0,0 +1,64 @@ +/* +=========================================================================== +Copyright (C) 2006-2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* contrast_fp.glsl */ + +uniform sampler2D u_ColorMap; + +const vec4 LUMINANCE_VECTOR = vec4(0.2125, 0.7154, 0.0721, 0.0); + +void main() +{ + vec2 st = gl_FragCoord.st; + + // calculate the screen texcoord in the 0.0 to 1.0 range + st *= r_FBufScale; + + // multiply with 4 because the FBO is only 1/4th of the screen resolution + st *= vec2(4.0, 4.0); + + // scale by the screen non-power-of-two-adjust + st *= r_NPOTScale; + + // calculate contrast color +#if 0 + // perform a box filter for the downsample + vec3 color = texture2D(u_ColorMap, st + vec2(-1.0, 1.0) * r_FBufScale).rgb; + color += texture2D(u_ColorMap, st + vec2(1.0, 1.0) * r_FBufScale).rgb; + color += texture2D(u_ColorMap, st + vec2(1.0, -1.0) * r_FBufScale).rgb; + color += texture2D(u_ColorMap, st + vec2(-1.0, -1.0) * r_FBufScale).rgb; + color *= 0.25; +#else + vec4 color = texture2D(u_ColorMap, st); +#endif + + float L = dot(LUMINANCE_VECTOR, color); + + // adjust contrast + L = pow(L, 1.32); + + float T = clamp(L - 0.71, 0.0, 1.0); + + color.rgb *= T; + + gl_FragColor = color; +} diff --git a/main/glsl/contrast_vp.glsl b/main/glsl/contrast_vp.glsl new file mode 100644 index 0000000000..b298383fe3 --- /dev/null +++ b/main/glsl/contrast_vp.glsl @@ -0,0 +1,33 @@ +/* +=========================================================================== +Copyright (C) 2006-2008 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* contrast_vp.glsl */ + +attribute vec4 attr_Position; + +uniform mat4 u_ModelViewProjectionMatrix; + +void main() +{ + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * attr_Position; +} diff --git a/main/glsl/debugShadowMap_fp.glsl b/main/glsl/debugShadowMap_fp.glsl new file mode 100644 index 0000000000..a01027be17 --- /dev/null +++ b/main/glsl/debugShadowMap_fp.glsl @@ -0,0 +1,61 @@ +/* +=========================================================================== +Copyright (C) 2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* debugShadowMap_fp.glsl */ + +uniform sampler2D u_ShadowMap; + +varying vec2 var_TexCoord; + +void main() +{ +#if defined(ESM) + + vec4 shadowMoments = texture2D(u_ShadowMap, var_TexCoord); + + float shadowDistance = shadowMoments.a; + + gl_FragColor = vec4(shadowDistance, 0.0, 0.0, 1.0); + +#elif defined(VSM) + vec4 shadowMoments = texture2D(u_ShadowMap, var_TexCoord); + + float shadowDistance = shadowMoments.r; + float shadowDistanceSquared = shadowMoments.a; + + gl_FragColor = vec4(shadowDistance, 0.0, 0.0, 1.0); + +#elif defined(EVSM) + + vec4 shadowMoments = texture2D(u_ShadowMap, var_TexCoord); + +#if defined(r_EVSMPostProcess) + float shadowDistance = shadowMoments.r; +#else + float shadowDistance = log(shadowMoments.b); +#endif + + gl_FragColor = vec4(shadowDistance, 0.0, 0.0, 1.0); +#else + discard; +#endif +} diff --git a/main/glsl/debugShadowMap_vp.glsl b/main/glsl/debugShadowMap_vp.glsl new file mode 100644 index 0000000000..b531674dd3 --- /dev/null +++ b/main/glsl/debugShadowMap_vp.glsl @@ -0,0 +1,38 @@ +/* +=========================================================================== +Copyright (C) 2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* debugShadowMap_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; + +uniform mat4 u_ModelViewProjectionMatrix; + +varying vec2 var_TexCoord; + +void main() +{ + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * attr_Position; + + var_TexCoord = attr_TexCoord0.st; +} diff --git a/main/glsl/deformVertexes_vp.glsl b/main/glsl/deformVertexes_vp.glsl new file mode 100644 index 0000000000..5d832b8485 --- /dev/null +++ b/main/glsl/deformVertexes_vp.glsl @@ -0,0 +1,212 @@ +/* +=========================================================================== +Copyright (C) 2009-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// deformVertexes_vp.glsl - Quake 3 deformVertexes semantic + + +uniform float u_DeformParms[MAX_SHADER_DEFORM_PARMS]; + +#if !defined(GLDRV_MESA) + +float triangle(float x) +{ + return max(1.0 - abs(x), 0); +} + +float sawtooth(float x) +{ + return x - floor(x); +} + +/* +vec4 DeformPosition(const int deformGen, + const vec4 wave, // [base amplitude phase freq] + const vec3 bulge, // [width height speed] + const float spread, + const float time, + const vec4 pos, + const vec3 normal, + const vec2 st) +{ + vec4 deformed = pos; + + if(deformGen == DGEN_WAVE_SIN) + { + float off = (pos.x + pos.y + pos.z) * spread; + float scale = wave.x + sin(off + wave.z + (time * wave.w)) * wave.y; + vec3 offset = normal * scale; + + deformed.xyz += offset; + } + + if(deformGen == DGEN_WAVE_SQUARE) + { + float off = (pos.x + pos.y + pos.z) * spread; + float scale = wave.x + sign(sin(off + wave.z + (time * wave.w))) * wave.y; + vec3 offset = normal * scale; + + deformed.xyz += offset; + } + + if(deformGen == DGEN_WAVE_TRIANGLE) + { + float off = (pos.x + pos.y + pos.z) * spread; + float scale = wave.x + triangle(off + wave.z + (time * wave.w)) * wave.y; + vec3 offset = normal * scale; + + deformed.xyz += offset; + } + + if(deformGen == DGEN_WAVE_SAWTOOTH) + { + float off = (pos.x + pos.y + pos.z) * spread; + float scale = wave.x + sawtooth(off + wave.z + (time * wave.w)) * wave.y; + vec3 offset = normal * scale; + + deformed.xyz += offset; + } + + if(deformGen == DGEN_WAVE_INVERSE_SAWTOOTH) + { + float off = (pos.x + pos.y + pos.z) * spread; + float scale = wave.x + (1.0 - sawtooth(off + wave.z + (time * wave.w))) * wave.y; + vec3 offset = normal * scale; + + deformed.xyz += offset; + } + + if(deformGen == DGEN_BULGE) + { + float bulgeWidth = bulge.x; + float bulgeHeight = bulge.y; + float bulgeSpeed = bulge.z; + + float now = time * bulgeSpeed; + + float off = (M_PI * 0.25) * st.x * bulgeWidth + now; + float scale = sin(off) * bulgeHeight; + vec3 offset = normal * scale; + + deformed.xyz += offset; + } + + return deformed; +} +*/ + +/* + define WAVEVALUE( table, base, amplitude, phase, freq ) \ + ((base) + table[ Q_ftol( ( ( (phase) + backEnd.refdef.floatTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) +*/ + +float WaveValue(float func, float base, float amplitude, float phase, float freq, float time) +{ + if(func == GF_SIN) + return base + sin(phase + (time * freq)) * amplitude; + + if(func == GF_SQUARE) + return base + sign(sin(phase + (time * freq))) * amplitude; + + if(func == GF_TRIANGLE) + return base + triangle(phase + (time * freq)) * amplitude; + + if(func == GF_SAWTOOTH) + return base + sawtooth(phase + (time * freq)) * amplitude; + + if(func == GF_INVERSE_SAWTOOTH) + return base + (1.0 - sawtooth(phase + (time * freq))) * amplitude; + + // if(func == GF_NOISE) + // TODO + + return 0.0; // GF_NONE +} + +vec4 DeformPosition2( const vec4 pos, + const vec3 normal, + const vec2 st, + float time) +{ + + int i, deformOfs = 0; + int numDeforms = int(u_DeformParms[deformOfs++]); + + vec4 deformed = pos; + + for(i = 0; i < numDeforms; i++) + { + int deformGen = int(u_DeformParms[deformOfs++]); + + + if(deformGen == DEFORM_WAVE) + { + float func = u_DeformParms[deformOfs++]; + float base = u_DeformParms[deformOfs++]; + float amplitude = u_DeformParms[deformOfs++]; + float phase = u_DeformParms[deformOfs++]; + float freq = u_DeformParms[deformOfs++]; + + float spread = u_DeformParms[deformOfs++]; + + float off = (pos.x + pos.y + pos.z) * spread; + float scale = WaveValue(func, base, amplitude, phase + off, freq, time); + vec3 offset = normal * scale; + + deformed.xyz += offset; + } + + if(deformGen == DEFORM_BULGE) + { + float bulgeWidth = u_DeformParms[deformOfs++]; + float bulgeHeight = u_DeformParms[deformOfs++]; + float bulgeSpeed = u_DeformParms[deformOfs++]; + + float now = time * bulgeSpeed; + + float off = (M_PI * 0.25) * st.x * bulgeWidth + now; + float scale = sin(off) * bulgeHeight; + vec3 offset = normal * scale; + + deformed.xyz += offset; + } + + if(deformGen == DEFORM_MOVE) + { + float func = u_DeformParms[deformOfs++]; + float base = u_DeformParms[deformOfs++]; + float amplitude = u_DeformParms[deformOfs++]; + float phase = u_DeformParms[deformOfs++]; + float freq = u_DeformParms[deformOfs++]; + + vec3 move = vec3(u_DeformParms[deformOfs++], u_DeformParms[deformOfs++], u_DeformParms[deformOfs++]); + float scale = WaveValue(func, base, amplitude, phase, freq, time); + vec3 offset = move * scale; + + deformed.xyz += offset; + } + } + + return deformed; +} + + +#endif // !defined(GLDRV_MESA) + diff --git a/main/glsl/depthToColor_fp.glsl b/main/glsl/depthToColor_fp.glsl new file mode 100644 index 0000000000..a4009c741e --- /dev/null +++ b/main/glsl/depthToColor_fp.glsl @@ -0,0 +1,57 @@ +/* +=========================================================================== +Copyright (C) 2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* depthToColor_fp.glsl */ + + +void main() +{ + // compute depth instead of world vertex position in a [0..1] range + float depth = gl_FragCoord.z; + +//#if 0 //defined(GLHW_ATI_DX10) || defined(GLHW_NV_DX10) +// gl_FragColor = vec4(0.0, 0.0, 0.0, depth); +//#else + // 32 bit precision + const vec4 bitSh = vec4(256 * 256 * 256, 256 * 256, 256, 1); + const vec4 bitMsk = vec4( 0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0); + + vec4 comp; + comp = depth * bitSh; + comp = fract(comp); + comp -= comp.xxyz * bitMsk; + gl_FragColor = comp; +/* + // 24 bit precision + const vec3 bitSh = vec3(256 * 256, 256, 1); + const vec3 bitMsk = vec3( 0, 1.0 / 256.0, 1.0 / 256.0); + + vec3 comp; + comp = depth * bitSh; + comp = fract(comp); + comp -= comp.xxy * bitMsk; + gl_FragColor = vec4(comp, 0.0); +*/ +//#endif +} + + \ No newline at end of file diff --git a/main/glsl/depthToColor_vp.glsl b/main/glsl/depthToColor_vp.glsl new file mode 100644 index 0000000000..a42aff042e --- /dev/null +++ b/main/glsl/depthToColor_vp.glsl @@ -0,0 +1,60 @@ +/* +=========================================================================== +Copyright (C) 2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* depthToColor_vp.glsl */ + +attribute vec4 attr_Position; +#if defined(r_VertexSkinning) +attribute vec4 attr_BoneIndexes; +attribute vec4 attr_BoneWeights; +uniform int u_VertexSkinning; +uniform mat4 u_BoneMatrix[MAX_GLSL_BONES]; +#endif + +uniform mat4 u_ModelViewProjectionMatrix; + +void main() +{ +#if defined(r_VertexSkinning) + if(bool(u_VertexSkinning)) + { + vec4 vertex = vec4(0.0); + + for(int i = 0; i < 4; i++) + { + int boneIndex = int(attr_BoneIndexes[i]); + float boneWeight = attr_BoneWeights[i]; + mat4 boneMatrix = u_BoneMatrix[boneIndex]; + + vertex += (boneMatrix * attr_Position) * boneWeight; + } + + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * vertex; + } + else +#endif + { + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * attr_Position; + } +} diff --git a/main/glsl/dispersion_C_fp.glsl b/main/glsl/dispersion_C_fp.glsl new file mode 100644 index 0000000000..8e8d15c6d3 --- /dev/null +++ b/main/glsl/dispersion_C_fp.glsl @@ -0,0 +1,67 @@ +/* +=========================================================================== +Copyright (C) 2006 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* dispersion_C_fp.glsl */ + +uniform samplerCube u_ColorMap; +uniform vec3 u_ViewOrigin; +uniform vec3 u_EtaRatio; +uniform float u_FresnelPower; +uniform float u_FresnelScale; +uniform float u_FresnelBias; + +varying vec3 var_Position; +varying vec3 var_Normal; + +void main() +{ + // compute incident ray + vec3 I = normalize(var_Position - u_ViewOrigin); + + // compute normal + vec3 N = normalize(var_Normal); // FIXME normalize? + + // compute reflection ray + vec3 R = reflect(I, N); + + // compute fresnel term + float fresnel = u_FresnelBias + pow(1.0 - dot(I, N), u_FresnelPower) * u_FresnelScale; + + // compute reflection color + vec3 reflectColor = textureCube(u_ColorMap, R).rgb; + + // compute refraction color using a refraction ray for each channel + vec3 refractColor; + + refractColor.r = textureCube(u_ColorMap, refract(I, N, u_EtaRatio.x)).r; + refractColor.g = textureCube(u_ColorMap, refract(I, N, u_EtaRatio.y)).g; + refractColor.b = textureCube(u_ColorMap, refract(I, N, u_EtaRatio.z)).b; + + // compute final color + vec4 color; + color.r = (1.0 - fresnel) * refractColor.r + reflectColor.r * fresnel; + color.g = (1.0 - fresnel) * refractColor.g + reflectColor.g * fresnel; + color.b = (1.0 - fresnel) * refractColor.b + reflectColor.b * fresnel; + color.a = 1.0; + + gl_FragColor = color; +} diff --git a/main/glsl/dispersion_C_vp.glsl b/main/glsl/dispersion_C_vp.glsl new file mode 100644 index 0000000000..7813257922 --- /dev/null +++ b/main/glsl/dispersion_C_vp.glsl @@ -0,0 +1,80 @@ +/* +=========================================================================== +Copyright (C) 2006-2008 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* dispersion_C_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec3 attr_Normal; +#if defined(r_VertexSkinning) +attribute vec4 attr_BoneIndexes; +attribute vec4 attr_BoneWeights; +uniform int u_VertexSkinning; +uniform mat4 u_BoneMatrix[MAX_GLSL_BONES]; +#endif + +uniform mat4 u_ModelMatrix; +uniform mat4 u_ModelViewProjectionMatrix; + +varying vec3 var_Position; +varying vec3 var_Normal; + +void main() +{ +#if defined(r_VertexSkinning) + if(bool(u_VertexSkinning)) + { + vec4 vertex = vec4(0.0); + vec3 normal = vec3(0.0); + + for(int i = 0; i < 4; i++) + { + int boneIndex = int(attr_BoneIndexes[i]); + float boneWeight = attr_BoneWeights[i]; + mat4 boneMatrix = u_BoneMatrix[boneIndex]; + + vertex += (boneMatrix * attr_Position) * boneWeight; + normal += (boneMatrix * vec4(attr_Normal, 0.0)).xyz * boneWeight; + } + + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * vertex; + + // transform position into world space + var_Position = (u_ModelMatrix * vertex).xyz; + + // transform normal into world space + var_Normal = (u_ModelMatrix * vec4(normal, 0.0)).xyz; + } + else +#endif + { + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * attr_Position; + + // transform position into world space + var_Position = (u_ModelMatrix * attr_Position).xyz; + + // transform normal into world space + var_Normal = (u_ModelMatrix * vec4(attr_Normal, 0.0)).xyz; + } +} + diff --git a/main/glsl/fogGlobal_fp.glsl b/main/glsl/fogGlobal_fp.glsl new file mode 100644 index 0000000000..fad6add8ff --- /dev/null +++ b/main/glsl/fogGlobal_fp.glsl @@ -0,0 +1,70 @@ +/* +=========================================================================== +Copyright (C) 2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* fogGlobal_fp.glsl */ + +uniform sampler2D u_ColorMap; // fog texture +uniform sampler2D u_DepthMap; +uniform vec3 u_ViewOrigin; +uniform vec4 u_FogDistanceVector; +uniform vec4 u_FogDepthVector; +uniform vec4 u_Color; +uniform mat4 u_ViewMatrix; +uniform mat4 u_UnprojectMatrix; + +void main() +{ + // calculate the screen texcoord in the 0.0 to 1.0 range + vec2 st = gl_FragCoord.st * r_FBufScale; + + // scale by the screen non-power-of-two-adjust + st *= r_NPOTScale; + + // reconstruct vertex position in world space + float depth = texture2D(u_DepthMap, st).r; + vec4 P = u_UnprojectMatrix * vec4(gl_FragCoord.xy, depth, 1.0); + P.xyz /= P.w; + +#if defined(COMPAT_ET) + // calculate the length in fog (t is always 0 if eye is in fog) + st.s = dot(P.xyz, u_FogDistanceVector.xyz) + u_FogDistanceVector.w; + // st.s = vertexDistanceToCamera; + st.t = 1.0; + + gl_FragColor = u_Color * texture2D(u_ColorMap, st); +#else + vec4 Pcam = u_ViewMatrix * vec4(P.xyz, 1.0); + float vertexDistanceToCamera = -Pcam.z; + + // float fogDistance = dot(P.xyz, u_FogDistanceVector.xyz) + u_FogDistanceVector.w; + // vertexDistanceToCamera = distance(P.xyz, u_ViewOrigin); + + // calculate fog exponent + float fogExponent = vertexDistanceToCamera * u_FogDepthVector.x; + + // calculate fog factor + float fogFactor = exp2(-abs(fogExponent)); + + // lerp between FBO color and fog color with GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA. GLS_DSTBLEND_SRC_ALPHA + gl_FragColor = vec4(u_Color.rgb, fogFactor); +#endif +} diff --git a/main/glsl/fogGlobal_vp.glsl b/main/glsl/fogGlobal_vp.glsl new file mode 100644 index 0000000000..93f8fb84ef --- /dev/null +++ b/main/glsl/fogGlobal_vp.glsl @@ -0,0 +1,33 @@ +/* +=========================================================================== +Copyright (C) 2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* fogGlobal_vp.glsl */ + +attribute vec4 attr_Position; + +uniform mat4 u_ModelViewProjectionMatrix; + +void main() +{ + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * attr_Position; +} diff --git a/main/glsl/fogQuake3_fp.glsl b/main/glsl/fogQuake3_fp.glsl new file mode 100644 index 0000000000..9bc535cf1b --- /dev/null +++ b/main/glsl/fogQuake3_fp.glsl @@ -0,0 +1,53 @@ +/* +=========================================================================== +Copyright (C) 2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* fogQuake3_fp.glsl */ + +uniform sampler2D u_ColorMap; +uniform vec4 u_PortalPlane; + +varying vec3 var_Position; +varying vec2 var_Tex; +varying vec4 var_Color; + +void main() +{ +#if defined(USE_PORTAL_CLIPPING) + { + float dist = dot(var_Position.xyz, u_PortalPlane.xyz) - u_PortalPlane.w; + if(dist < 0.0) + { + discard; + return; + } + } +#endif + + vec4 color = texture2D(u_ColorMap, var_Tex); + + color *= var_Color; + gl_FragColor = color; + +#if 0 + gl_FragColor = vec4(vec3(1.0, 0.0, 0.0), color.a); +#endif +} diff --git a/main/glsl/fogQuake3_vp.glsl b/main/glsl/fogQuake3_vp.glsl new file mode 100644 index 0000000000..e8d24772a0 --- /dev/null +++ b/main/glsl/fogQuake3_vp.glsl @@ -0,0 +1,119 @@ +/* +=========================================================================== +Copyright (C) 2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* fogQuake3_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; +attribute vec3 attr_Normal; +attribute vec4 attr_Color; + +attribute vec4 attr_Position2; +attribute vec3 attr_Normal2; + +uniform float u_VertexInterpolation; + +uniform vec3 u_ViewOrigin; + +uniform float u_Time; + +uniform vec4 u_ColorModulate; +uniform vec4 u_Color; +uniform mat4 u_ModelMatrix; +uniform mat4 u_ModelViewProjectionMatrix; + +uniform vec4 u_FogDistanceVector; +uniform vec4 u_FogDepthVector; +uniform float u_FogEyeT; + +varying vec3 var_Position; +varying vec2 var_Tex; +varying vec4 var_Color; + + + + + +void main() +{ + vec4 position; + vec3 normal; + +#if defined(USE_VERTEX_SKINNING) + + VertexSkinning_P_N( attr_Position, attr_Normal, + position, normal); + +#elif defined(USE_VERTEX_ANIMATION) + + VertexAnimation_P_N(attr_Position, attr_Position2, + attr_Normal, attr_Normal2, + u_VertexInterpolation, + position, normal); + +#else + position = attr_Position; + normal = attr_Normal; +#endif + +#if defined(USE_DEFORM_VERTEXES) + position = DeformPosition2( position, + normal, + attr_TexCoord0.st, + u_Time); +#endif + + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * position; + + // transform position into world space + var_Position = (u_ModelMatrix * position).xyz; + + // calculate the length in fog + float s = dot(position.xyz, u_FogDistanceVector.xyz) + u_FogDistanceVector.w; + float t = dot(position.xyz, u_FogDepthVector.xyz) + u_FogDepthVector.w; + + // partially clipped fogs use the T axis +#if defined(EYE_OUTSIDE) + if(t < 1.0) + { + t = 1.0 / 32; // point is outside, so no fogging + } + else + { + t = 1.0 / 32 + 30.0 / 32 * t / (t - u_FogEyeT); // cut the distance at the fog plane + } +#else + if(t < 0) + { + t = 1.0 / 32; // point is outside, so no fogging + } + else + { + t = 31.0 / 32; + } +#endif + + var_Tex = vec2(s, t); + + var_Color = /* attr_Color * u_ColorModulate + */ u_Color; +} diff --git a/main/glsl/forwardLighting_fp.glsl b/main/glsl/forwardLighting_fp.glsl new file mode 100644 index 0000000000..4d8e334c77 --- /dev/null +++ b/main/glsl/forwardLighting_fp.glsl @@ -0,0 +1,1107 @@ +/* +=========================================================================== +Copyright (C) 2007-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* forwardLighting_fp.glsl */ + +uniform sampler2D u_DiffuseMap; +uniform sampler2D u_NormalMap; +uniform sampler2D u_SpecularMap; +uniform sampler2D u_AttenuationMapXY; +uniform sampler2D u_AttenuationMapZ; + +#if defined(LIGHT_DIRECTIONAL) +uniform sampler2D u_ShadowMap0; +uniform sampler2D u_ShadowMap1; +uniform sampler2D u_ShadowMap2; +uniform sampler2D u_ShadowMap3; +uniform sampler2D u_ShadowMap4; +#elif defined(LIGHT_PROJ) +uniform sampler2D u_ShadowMap0; +#else +uniform samplerCube u_ShadowMap; +#endif + +uniform sampler2D u_RandomMap; // random normals + + +uniform vec3 u_ViewOrigin; + +#if defined(LIGHT_DIRECTIONAL) +uniform vec3 u_LightDir; +#else +uniform vec3 u_LightOrigin; +#endif +uniform vec3 u_LightColor; +uniform float u_LightRadius; +uniform float u_LightScale; +uniform float u_LightWrapAround; + +uniform mat4 u_ShadowMatrix[MAX_SHADOWMAPS]; +uniform vec4 u_ShadowParallelSplitDistances; +uniform float u_ShadowTexelSize; +uniform float u_ShadowBlur; + +uniform mat4 u_ViewMatrix; + +uniform vec4 u_PortalPlane; + +uniform float u_DepthScale; + +varying vec3 var_Position; +varying vec4 var_TexDiffuse; +varying vec4 var_TexNormal; +#if defined(USE_NORMAL_MAPPING) +varying vec2 var_TexSpecular; +#endif +varying vec4 var_TexAttenuation; +#if defined(USE_NORMAL_MAPPING) +varying vec4 var_Tangent; +varying vec4 var_Binormal; +#endif +varying vec4 var_Normal; +//varying vec4 var_Color; + + + +/* +================ +MakeNormalVectors + +Given a normalized forward vector, create two +other perpendicular vectors +================ +*/ +void MakeNormalVectors(const vec3 forward, inout vec3 right, inout vec3 up) +{ + // this rotate and negate guarantees a vector + // not colinear with the original + right.y = -forward.x; + right.z = forward.y; + right.x = forward.z; + + float d = dot(right, forward); + right += forward * -d; + normalize(right); + up = cross(right, forward); // GLSL cross product is the same as in Q3A +} + +float Rand(vec2 co) +{ + return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); +} + +float Noise(vec2 co) +{ + return Rand(floor(co * 128.0)); +} + +vec3 RandomVec3(vec2 uv) +{ + vec3 dir; + +#if 1 + float r = Rand(uv); + float angle = 2.0 * M_PI * r;// / 360.0; + + dir = normalize(vec3(cos(angle), sin(angle), r)); +#else + // dir = texture2D(u_NoiseMap, gl_FragCoord.st * r_FBufScale).rgb; + dir = normalize(2.0 * (texture2D(u_RandomMap, uv).xyz - 0.5)); +#endif + + return dir; +} + + +/* +source: http://en.wikipedia.org/wiki/Chebyshev%27s_inequality + +X = distribution +mu = mean +sigma = standard deviation + +=> then for any real number k > 0: + +Pr(X -mu >= k * sigma) <= 1 / ( 1 + k^2) +*/ + +#if defined(VSM) || defined(EVSM) +float ChebyshevUpperBound(vec2 shadowMoments, float vertexDistance, float minVariance) +{ + float shadowDistance = shadowMoments.x; + float shadowDistanceSquared = shadowMoments.y; + + // compute variance + float E_x2 = shadowDistanceSquared; + float Ex_2 = shadowDistance * shadowDistance; + + float variance = max(E_x2 - Ex_2, max(minVariance, VSM_EPSILON)); + // float variance = smoothstep(minVariance, 1.0, max(E_x2 - Ex_2, 0.0)); + + // compute probabilistic upper bound + float d = vertexDistance - shadowDistance; + float pMax = variance / (variance + (d * d)); + + /* + #if defined(r_LightBleedReduction) + pMax = smoothstep(r_LightBleedReduction, 1.0, pMax); + #endif + */ + + // one-tailed Chebyshev with k > 0 + return (vertexDistance <= shadowDistance ? 1.0 : pMax); +} +#endif + + +#if defined(EVSM) +vec2 WarpDepth(float depth) +{ + // rescale depth into [-1, 1] + depth = 2.0 * depth - 1.0; + float pos = exp( r_EVSMExponents.x * depth); + float neg = -exp(-r_EVSMExponents.y * depth); + + return vec2(pos, neg); +} + +vec4 ShadowDepthToEVSM(float depth) +{ + vec2 warpedDepth = WarpDepth(depth); + return vec4(warpedDepth.xy, warpedDepth.xy * warpedDepth.xy); +} +#endif // #if defined(EVSM) + + + + + + +#if defined(LIGHT_DIRECTIONAL) + +void FetchShadowMoments(vec3 Pworld, inout vec4 shadowVert, inout vec4 shadowMoments) +{ + // vec4 shadowVert; + // vec4 shadowMoments; + + // transform to camera space + vec4 Pcam = u_ViewMatrix * vec4(Pworld.xyz, 1.0); + float vertexDistanceToCamera = -Pcam.z; + +#if defined(r_ParallelShadowSplits_1) + if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.x) + { + shadowVert = u_ShadowMatrix[0] * vec4(Pworld.xyz, 1.0); + shadowMoments = texture2DProj(u_ShadowMap0, shadowVert.xyw); + } + else + { + shadowVert = u_ShadowMatrix[1] * vec4(Pworld.xyz, 1.0); + shadowMoments = texture2DProj(u_ShadowMap1, shadowVert.xyw); + } +#elif defined(r_ParallelShadowSplits_2) + if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.x) + { + shadowVert = u_ShadowMatrix[0] * vec4(Pworld.xyz, 1.0); + shadowMoments = texture2DProj(u_ShadowMap0, shadowVert.xyw); + } + else if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.y) + { + shadowVert = u_ShadowMatrix[1] * vec4(Pworld.xyz, 1.0); + shadowMoments = texture2DProj(u_ShadowMap1, shadowVert.xyw); + + } + else + { + shadowVert = u_ShadowMatrix[2] * vec4(Pworld.xyz, 1.0); + shadowMoments = texture2DProj(u_ShadowMap2, shadowVert.xyw); + } +#elif defined(r_ParallelShadowSplits_3) + if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.x) + { + shadowVert = u_ShadowMatrix[0] * vec4(Pworld.xyz, 1.0); + shadowMoments = texture2DProj(u_ShadowMap0, shadowVert.xyw); + } + else if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.y) + { + shadowVert = u_ShadowMatrix[1] * vec4(Pworld.xyz, 1.0); + shadowMoments = texture2DProj(u_ShadowMap1, shadowVert.xyw); + } + else if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.z) + { + shadowVert = u_ShadowMatrix[2] * vec4(Pworld.xyz, 1.0); + shadowMoments = texture2DProj(u_ShadowMap2, shadowVert.xyw); + } + else + { + shadowVert = u_ShadowMatrix[3] * vec4(Pworld.xyz, 1.0); + shadowMoments = texture2DProj(u_ShadowMap3, shadowVert.xyw); + } +#elif defined(r_ParallelShadowSplits_4) + if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.x) + { + shadowVert = u_ShadowMatrix[0] * vec4(Pworld.xyz, 1.0); + shadowMoments = texture2DProj(u_ShadowMap0, shadowVert.xyw); + } + else if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.y) + { + shadowVert = u_ShadowMatrix[1] * vec4(Pworld.xyz, 1.0); + shadowMoments = texture2DProj(u_ShadowMap1, shadowVert.xyw); + } + else if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.z) + { + shadowVert = u_ShadowMatrix[2] * vec4(Pworld.xyz, 1.0); + shadowMoments = texture2DProj(u_ShadowMap2, shadowVert.xyw); + } + else if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.w) + { + shadowVert = u_ShadowMatrix[3] * vec4(Pworld.xyz, 1.0); + shadowMoments = texture2DProj(u_ShadowMap3, shadowVert.xyw); + } + else + { + shadowVert = u_ShadowMatrix[4] * vec4(Pworld.xyz, 1.0); + shadowMoments = texture2DProj(u_ShadowMap4, shadowVert.xyw); + } +#else + { + shadowVert = u_ShadowMatrix[0] * vec4(Pworld.xyz, 1.0); + shadowMoments = texture2DProj(u_ShadowMap0, shadowVert.xyw); + } +#endif + +#if defined(EVSM) && defined(r_EVSMPostProcess) + shadowMoments = ShadowDepthToEVSM(shadowMoments.x); +#endif + + // return shadowMoments; +} + +#if defined(r_PCFSamples) +vec4 PCF(vec3 Pworld, float filterWidth, float samples) +{ + vec3 forward, right, up; + + // filterWidth *= u_LightRadius; + + forward = normalize(-u_LightDir); + MakeNormalVectors(forward, right, up); + + vec4 moments = vec4(0.0, 0.0, 0.0, 0.0); + +#if 0 + // compute step size for iterating through the kernel + float stepSize = 2.0 * filterWidth / samples; + + for(float i = -filterWidth; i < filterWidth; i += stepSize) + { + for(float j = -filterWidth; j < filterWidth; j += stepSize) + { + vec4 shadowVert; + vec4 shadowMoments; + FetchShadowMoments(Pworld + right * i + up * j, shadowVert, shadowMoments); + moments += shadowMoments; + } + } +#else + for(int i = 0; i < samples; i++) + { + for(int j = 0; j < samples; j++) + { + vec3 rand = RandomVec3(gl_FragCoord.st * r_FBufScale + vec2(i, j)) * filterWidth; + // rand.z = 0; + // rand = normalize(rand) * filterWidth; + + vec4 shadowVert; + vec4 shadowMoments; + FetchShadowMoments(Pworld + right * rand.x + up * rand.y, shadowVert, shadowMoments); + moments += shadowMoments; + } + } +#endif + + // return average of the samples + moments *= (1.0 / (samples * samples)); + return moments; +} +#endif // #if defined(r_PCFSamples) + + + +#elif defined(LIGHT_PROJ) + +vec4 FetchShadowMoments(vec2 st) +{ +#if defined(EVSM) && defined(r_EVSMPostProcess) + return ShadowDepthToEVSM(texture2D(u_ShadowMap0, st).a); +#else + return texture2D(u_ShadowMap0, st); +#endif +} + +#if defined(r_PCFSamples) +vec4 PCF(vec4 shadowVert, float filterWidth, float samples) +{ + vec4 moments = vec4(0.0, 0.0, 0.0, 0.0); + +#if 0 + // compute step size for iterating through the kernel + float stepSize = 2.0 * filterWidth / samples; + + for(float i = -filterWidth; i < filterWidth; i += stepSize) + { + for(float j = -filterWidth; j < filterWidth; j += stepSize) + { + moments += FetchShadowMoments(shadowVert.xy / shadowVert.w + vec2(i, j)); + } + } +#else + for(int i = 0; i < samples; i++) + { + for(int j = 0; j < samples; j++) + { + vec3 rand = RandomVec3(gl_FragCoord.st * r_FBufScale + vec2(i, j)) * filterWidth; + // rand = vec3(0.0, 0.0, 1.0); + // rand.z = 0; + // rand = normalize(rand);// * filterWidth; + + moments += FetchShadowMoments(shadowVert.xy / shadowVert.w + rand.xy); + } + } +#endif + + // return average of the samples + moments *= (1.0 / (samples * samples)); + return moments; +} +#endif // #if defined(r_PCFSamples) + +#else + +vec4 FetchShadowMoments(vec3 I) +{ +#if defined(EVSM) && defined(r_EVSMPostProcess) + return ShadowDepthToEVSM(textureCube(u_ShadowMap, I).a); +#else + return textureCube(u_ShadowMap, I); +#endif +} + +#if defined(r_PCFSamples) +vec4 PCF(vec3 I, float filterWidth, float samples) +{ + vec3 forward, right, up; + + forward = normalize(I); + MakeNormalVectors(forward, right, up); + + vec4 moments = vec4(0.0, 0.0, 0.0, 0.0); + +#if 0 + // compute step size for iterating through the kernel + float stepSize = 2.0 * filterWidth / samples; + + for(float i = -filterWidth; i < filterWidth; i += stepSize) + { + for(float j = -filterWidth; j < filterWidth; j += stepSize) + { + moments += FetchShadowMoments(I + right * i + up * j); + } + } +#else + for(int i = 0; i < samples; i++) + { + for(int j = 0; j < samples; j++) + { + vec3 rand = RandomVec3(gl_FragCoord.st * r_FBufScale + vec2(i, j)) * filterWidth; + // rand.z = 0; + // rand = normalize(rand) * filterWidth; + + moments += FetchShadowMoments(I + right * rand.x + up * rand.y); + } + } +#endif + + // return average of the samples + moments *= (1.0 / (samples * samples)); + return moments; +} +#endif // #if defined(r_PCFSamples) + +#endif + + +#if defined(PCSS) + + +#if defined(LIGHT_DIRECTIONAL) +// TODO SumBlocker for sun shadowing + +#elif defined(LIGHT_PROJ) +float SumBlocker(vec4 shadowVert, float vertexDistance, float filterWidth, float samples) +{ + float stepSize = 2.0 * filterWidth / samples; + + float blockerCount = 0.0; + float blockerSum = 0.0; + + for(float i = -filterWidth; i < filterWidth; i += stepSize) + { + for(float j = -filterWidth; j < filterWidth; j += stepSize) + { + float shadowDistance = texture2DProj(u_ShadowMap0, vec3(shadowVert.xy + vec2(i, j), shadowVert.w)).x; + // float shadowDistance = texture2D(u_ShadowMap, shadowVert.xy / shadowVert.w + vec2(i, j)).x; + + // FIXME VSM_CLAMP + + if(vertexDistance > shadowDistance) + { + blockerCount += 1.0; + blockerSum += shadowDistance; + } + } + } + + float result; + if(blockerCount > 0.0) + result = blockerSum / blockerCount; + else + result = 0.0; + + return result; +} +#else +// case LIGHT_OMNI +float SumBlocker(vec3 I, float vertexDistance, float filterWidth, float samples) +{ + vec3 forward, right, up; + + forward = normalize(I); + MakeNormalVectors(forward, right, up); + + float stepSize = 2.0 * filterWidth / samples; + + float blockerCount = 0.0; + float blockerSum = 0.0; + + for(float i = -filterWidth; i < filterWidth; i += stepSize) + { + for(float j = -filterWidth; j < filterWidth; j += stepSize) + { + float shadowDistance = textureCube(u_ShadowMap, I + right * i + up * j).x; + + if(vertexDistance > shadowDistance) + { + blockerCount += 1.0; + blockerSum += shadowDistance; + } + } + } + + float result; + if(blockerCount > 0.0) + result = blockerSum / blockerCount; + else + result = -1.0; + + return result; +} +#endif + +float EstimatePenumbra(float vertexDistance, float blocker) +{ + float penumbra; + + if(blocker == 0.0) + penumbra = 0.0; + else + penumbra = ((vertexDistance - blocker) * u_LightRadius) / blocker; + + return penumbra; +} + +#endif + + +/* +Some explanations by Marco Salvi about exponential shadow mapping: + +Now you are filtering exponential values which rapidly go out of range, +to avoid this issue you can filter the logarithm of these values (and the go back to exp space) + +For example if you averaging two exponential value such as exp(A) and exp(B) you have: + +a*exp(A) + b*exp(B) (a and b are some filter weights) + +but you can rewrite the same expression as: + +exp(A) * (a + b*exp(B-A)) , + +exp(A) * exp( log (a + b*exp(B-A)))), + +and: + +exp(A + log(a + b*exp(B-A)) + +Now your sum of exponential is written as a single exponential, if you take the logarithm of it you can then just work on its argument: + +A + log(a + b*exp(B-A)) + +Basically you end up filtering the argument of your exponential functions, which are just linear depth values, +so you enjoy the same range you have with less exotic techniques. +Just don't forget to go back to exp space when you use the final filtered value. + + +Though hardware texture filtering is not mathematically correct in log space it just causes a some overdarkening, nothing major. + +If you have your shadow map filtered in log space occlusion is just computed like this (let assume we use bilinear filtering): + +float occluder = tex2D( esm_sampler, esm_uv ); +float occlusion = exp( occluder - receiver ); + +while with filtering in exp space you have: + +float exp_occluder = tex2D( esm_sampler, esm_uv ); +float occlusion = exp_occluder / exp( receiver ); + +EDIT: if more complex filters are used (trilinear, aniso, with mip maps) you need to generate mip maps using log filteirng as well. +*/ + +/* +float log_conv(float x0, float X, float y0, float Y) +{ + return (X + log(x0 + (y0 * exp(Y - X)))); +} +*/ + +void main() +{ +#if defined(USE_PORTAL_CLIPPING) + { + float dist = dot(var_Position.xyz, u_PortalPlane.xyz) - u_PortalPlane.w; + if(dist < 0.0) + { + discard; + return; + } + } +#endif + + +#if 0 + // create random noise vector + vec3 rand = RandomVec3(gl_FragCoord.st * r_FBufScale); + + gl_FragColor = vec4(rand * 0.5 + 0.5, 1.0); + return; +#endif + + + float shadow = 1.0; +#if defined(USE_SHADOWING) + +#if defined(LIGHT_DIRECTIONAL) + + + vec4 shadowVert; + vec4 shadowMoments; + FetchShadowMoments(var_Position.xyz, shadowVert, shadowMoments); + + // FIXME + #if 0 // defined(r_PCFSamples) + shadowMoments = PCF(var_Position.xyz, u_ShadowTexelSize * u_ShadowBlur, r_PCFSamples); + #endif + +#if 0 + gl_FragColor = vec4(u_ShadowTexelSize * u_ShadowBlur * u_LightRadius, 0.0, 0.0, 1.0); + return; +#endif + +#if defined(r_ShowParallelShadowSplits) + // transform to camera space + vec4 Pcam = u_ViewMatrix * vec4(var_Position.xyz, 1.0); + float vertexDistanceToCamera = -Pcam.z; + +#if defined(r_ParallelShadowSplits_1) + if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.x) + { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + return; + } + else + { + gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0); + return; + } +#elif defined(r_ParallelShadowSplits_2) + if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.x) + { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + return; + } + else if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.y) + { + gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); + return; + } + else + { + gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0); + return; + } +#elif defined(r_ParallelShadowSplits_3) + if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.x) + { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + return; + } + else if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.y) + { + gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); + return; + } + else if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.z) + { + gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); + return; + } + else + { + gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0); + return; + } +#elif defined(r_ParallelShadowSplits_4) + if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.x) + { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + return; + } + else if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.y) + { + gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); + return; + } + else if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.z) + { + gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); + return; + } + else if(vertexDistanceToCamera < u_ShadowParallelSplitDistances.w) + { + gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); + return; + } + else + { + gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0); + return; + } +#else + { + gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0); + return; + } +#endif +#endif // #if defined(r_ShowParallelShadowSplits) + +#elif defined(LIGHT_PROJ) + + vec4 shadowVert = u_ShadowMatrix[0] * vec4(var_Position.xyz, 1.0); + + // compute incident ray + vec3 I = var_Position.xyz - u_LightOrigin; + + const float SHADOW_BIAS = 0.001; + float vertexDistance = length(I) / u_LightRadius - SHADOW_BIAS; + + #if defined(r_PCFSamples) + vec4 shadowMoments = PCF(shadowVert, u_ShadowTexelSize * u_ShadowBlur, r_PCFSamples); + + /* + #elif defined(PCSS) + + // step 1: find blocker estimate + + float blockerSearchWidth = u_ShadowTexelSize * u_LightRadius / vertexDistance; + float blockerSamples = 6.0; // how many samples to use for blocker search + float blocker = SumBlocker(shadowVert, vertexDistance, blockerSearchWidth, blockerSamples); + + #if 0 + // uncomment to visualize blockers + gl_FragColor = vec4(blocker * 0.3, 0.0, 0.0, 1.0); + return; + #endif + + // step 2: estimate penumbra using parallel planes approximation + float penumbra = EstimatePenumbra(vertexDistance, blocker); + + #if 0 + // uncomment to visualize penumbrae + gl_FragColor = vec4(0.0, 0.0, penumbra, 1.0); + return; + #endif + + // step 3: compute percentage-closer filter + vec4 shadowMoments; + if(penumbra > 0.0) + { + const float PCFsamples = 4.0; + + + //float maxpen = PCFsamples * (1.0 / u_ShadowTexelSize); + //if(penumbra > maxpen) + // penumbra = maxpen; + // + + shadowMoments = PCF(shadowVert, penumbra, PCFsamples); + } + else + { + shadowMoments = texture2DProj(u_ShadowMap, shadowVert.xyw); + } + */ + #else + // no filter + vec4 shadowMoments = FetchShadowMoments(shadowVert.xy / shadowVert.w); + #endif + +#else + // compute incident ray + vec3 I = var_Position.xyz - u_LightOrigin; + + // const float SHADOW_BIAS = 0.01; + // float vertexDistance = length(I) / u_LightRadius - 0.01; + +#if 0 + gl_FragColor = vec4(u_ShadowTexelSize * u_ShadowBlur * length(I), 0.0, 0.0, 1.0); + return; +#endif + + #if defined(r_PCFSamples) + vec4 shadowMoments = PCF(I, u_ShadowTexelSize * u_ShadowBlur * length(I), r_PCFSamples); + + /* + #elif defined(PCSS) + + // step 1: find blocker estimate + + float blockerSearchWidth = u_ShadowTexelSize * u_LightRadius / vertexDistance; + float blockerSamples = 6.0; // how many samples to use for blocker search + float blocker = SumBlocker(I, vertexDistance, blockerSearchWidth, blockerSamples); + + #if 0 + // visualize blockers + gl_FragColor = vec4(blocker * 0.3, 0.0, 0.0, 1.0); + return; + #endif + + // step 2: estimate penumbra using parallel planes approximation + float penumbra = EstimatePenumbra(vertexDistance, blocker); + + #if 0 + // visualize penumbrae + // if(penumbra > 1.0) + gl_FragColor = vec4(0.0, 0.0, penumbra, 1.0); + return; + #endif + + // step 3: compute percentage-closer filter + // vec4 shadowMoments; + vec4 shadowMoments; // = textureCube(u_ShadowMap, I); + + if(penumbra > 0.0 && blocker > -1.0) + { + const float PCFsamples = 2.0; + + // float maxpen = PCFsamples * (1.0 / u_ShadowTexelSize); + // if(penumbra > maxpen) + // penumbra = maxpen; + // + + // shadowMoments = PCF(I, penumbra, PCFsamples); + shadowMoments = PCF(I, u_ShadowTexelSize * u_ShadowBlur * penumbra, PCFsamples); + } + else + { + shadowMoments = textureCube(u_ShadowMap, I); + } + */ + + #else + // no extra filtering, single tap + vec4 shadowMoments = FetchShadowMoments(I); + #endif +#endif + + + + +#if defined(ESM) + { + const float SHADOW_BIAS = 0.001; + +#if defined(LIGHT_DIRECTIONAL) + float vertexDistance = shadowVert.z - SHADOW_BIAS; // * r_ShadowMapDepthScale; +#else + float vertexDistance = (length(I) / u_LightRadius) - SHADOW_BIAS; // * r_ShadowMapDepthScale; +#endif + + float shadowDistance = shadowMoments.a; + + // standard shadow mapping + shadow = vertexDistance <= shadowDistance ? 1.0 : 0.0; + + // exponential shadow mapping + // shadow = clamp(exp(r_OverDarkeningFactor * (shadowDistance - log(vertexDistance))), 0.0, 1.0); + // shadow = clamp(exp(r_OverDarkeningFactor * shadowDistance) * exp(-r_OverDarkeningFactor * vertexDistance), 0.0, 1.0); + // shadow = smoothstep(0.0, 1.0, shadow); + + #if defined(r_DebugShadowMaps) + #extension GL_EXT_gpu_shader4 : enable + gl_FragColor.r = (r_DebugShadowMaps & 1) != 0 ? shadowDistance : 0.0; + gl_FragColor.g = (r_DebugShadowMaps & 2) != 0 ? -(shadowDistance - vertexDistance) : 0.0; + gl_FragColor.b = (r_DebugShadowMaps & 4) != 0 ? shadow : 0.0; + gl_FragColor.a = 1.0; + return; + #endif + } +#elif defined(VSM) + { + #if defined(VSM_CLAMP) + // convert to [-1, 1] vector space + shadowMoments = 2.0 * (shadowMoments - 0.5); + #endif + + const float SHADOW_BIAS = 0.001; + +#if defined(LIGHT_DIRECTIONAL) + float vertexDistance = shadowVert.z - SHADOW_BIAS; +#else + float vertexDistance = length(I) / u_LightRadius - SHADOW_BIAS; +#endif + + shadow = ChebyshevUpperBound(shadowMoments.ra, vertexDistance, VSM_EPSILON); + } +#elif defined(EVSM) + { + const float SHADOW_BIAS = 0.001; + +#if defined(LIGHT_DIRECTIONAL) + float vertexDistance = shadowVert.z - 0.0001; +#else + float vertexDistance = (length(I) / u_LightRadius) - SHADOW_BIAS; // * r_ShadowMapDepthScale;// - SHADOW_BIAS; +#endif + + vec2 warpedVertexDistances = WarpDepth(vertexDistance); + + // derivative of warping at depth + vec2 depthScale = VSM_EPSILON * r_EVSMExponents * warpedVertexDistances; + vec2 minVariance = depthScale * depthScale; + + float posContrib = ChebyshevUpperBound(shadowMoments.xz, warpedVertexDistances.x, minVariance.x); + float negContrib = ChebyshevUpperBound(shadowMoments.yw, warpedVertexDistances.y, minVariance.y); + + shadow = min(posContrib, negContrib); + + #if defined(r_DebugShadowMaps) + #extension GL_EXT_gpu_shader4 : enable + gl_FragColor.r = (r_DebugShadowMaps & 1) != 0 ? posContrib : 0.0; + gl_FragColor.g = (r_DebugShadowMaps & 2) != 0 ? negContrib : 0.0; + gl_FragColor.b = (r_DebugShadowMaps & 4) != 0 ? shadow : 0.0; + gl_FragColor.a = 1.0; + return; + #endif + + } +#endif + + if(shadow <= 0.0) + { + discard; + return; + } + +#endif // USE_SHADOWING + + // compute light direction in world space +#if defined(LIGHT_DIRECTIONAL) + vec3 L = u_LightDir; +#else + vec3 L = normalize(u_LightOrigin - var_Position); +#endif + + vec2 texDiffuse = var_TexDiffuse.st; + +#if defined(USE_NORMAL_MAPPING) + + // invert tangent space for twosided surfaces + mat3 tangentToWorldMatrix; +#if defined(TWOSIDED) + if(gl_FrontFacing) + { + tangentToWorldMatrix = mat3(-var_Tangent.xyz, -var_Binormal.xyz, -var_Normal.xyz); + } + else +#endif + { + tangentToWorldMatrix = mat3(var_Tangent.xyz, var_Binormal.xyz, var_Normal.xyz); + } + + + vec2 texNormal = var_TexNormal.st; + vec2 texSpecular = var_TexSpecular.st; + + // compute view direction in world space + vec3 V = normalize(u_ViewOrigin - var_Position.xyz); + +#if defined(USE_PARALLAX_MAPPING) + + // ray intersect in view direction + + mat3 worldToTangentMatrix; + #if defined(GLHW_ATI) || defined(GLHW_ATI_DX10) || defined(GLDRV_MESA) + worldToTangentMatrix = mat3(tangentToWorldMatrix[0][0], tangentToWorldMatrix[1][0], tangentToWorldMatrix[2][0], + tangentToWorldMatrix[0][1], tangentToWorldMatrix[1][1], tangentToWorldMatrix[2][1], + tangentToWorldMatrix[0][2], tangentToWorldMatrix[1][2], tangentToWorldMatrix[2][2]); + #else + worldToTangentMatrix = transpose(tangentToWorldMatrix); + #endif + + // compute view direction in tangent space + vec3 Vts = worldToTangentMatrix * V; + Vts = normalize(Vts); + + // size and start position of search in texture space + vec2 S = Vts.xy * -u_DepthScale / Vts.z; + + float depth = RayIntersectDisplaceMap(texNormal, S, u_NormalMap); + + // compute texcoords offset + vec2 texOffset = S * depth; + + texDiffuse.st += texOffset; + texNormal.st += texOffset; + texSpecular.st += texOffset; +#endif // USE_PARALLAX_MAPPING + + // compute half angle in world space + vec3 H = normalize(L + V); + + // compute normal in tangent space from normalmap + vec3 N = 2.0 * (texture2D(u_NormalMap, texNormal.st).xyz - 0.5); + #if defined(r_NormalScale) + N.z *= r_NormalScale; + normalize(N); + #endif + + // transform normal into world space + N = tangentToWorldMatrix * N; + + +#else // USE_NORMAL_MAPPING + + vec3 N; +#if defined(TWOSIDED) + if(gl_FrontFacing) + { + N = -normalize(var_Normal.xyz); + } + else +#endif + { + N = normalize(var_Normal.xyz); + } + +#endif // USE_NORMAL_MAPPING + + // compute the light term +#if defined(r_WrapAroundLighting) + float NL = clamp(dot(N, L) + u_LightWrapAround, 0.0, 1.0) / clamp(1.0 + u_LightWrapAround, 0.0, 1.0); +#else + float NL = clamp(dot(N, L), 0.0, 1.0); +#endif + + // compute the diffuse term + vec4 diffuse = texture2D(u_DiffuseMap, texDiffuse.st); + diffuse.rgb *= u_LightColor * NL; + +#if defined(USE_NORMAL_MAPPING) + // compute the specular term + vec3 specular = texture2D(u_SpecularMap, texSpecular).rgb * u_LightColor * pow(clamp(dot(N, H), 0.0, 1.0), r_SpecularExponent) * r_SpecularScale; +#endif + + + + // compute light attenuation +#if defined(LIGHT_PROJ) + vec3 attenuationXY = texture2DProj(u_AttenuationMapXY, var_TexAttenuation.xyw).rgb; + vec3 attenuationZ = texture2D(u_AttenuationMapZ, vec2(var_TexAttenuation.z + 0.5, 0.0)).rgb; // FIXME + +#elif defined(LIGHT_DIRECTIONAL) + vec3 attenuationXY = vec3(1.0); + vec3 attenuationZ = vec3(1.0); + +#else + vec3 attenuationXY = texture2D(u_AttenuationMapXY, var_TexAttenuation.xy).rgb; + vec3 attenuationZ = texture2D(u_AttenuationMapZ, vec2(var_TexAttenuation.z, 0)).rgb; +#endif + + // compute final color + vec4 color = diffuse; + +#if defined(USE_NORMAL_MAPPING) + color.rgb += specular; +#endif + +#if !defined(LIGHT_DIRECTIONAL) + color.rgb *= attenuationXY; + color.rgb *= attenuationZ; +#endif + + color.rgb *= u_LightScale; + color.rgb *= shadow; + + color.r *= var_TexDiffuse.p; + color.gb *= var_TexNormal.pq; + + gl_FragColor = color; + +#if 0 +#if defined(USE_PARALLAX_MAPPING) + gl_FragColor = vec4(vec3(1.0, 0.0, 0.0), diffuse.a); +#elif defined(USE_NORMAL_MAPPING) + gl_FragColor = vec4(vec3(0.0, 0.0, 1.0), diffuse.a); +#else + gl_FragColor = vec4(vec3(0.0, 1.0, 0.0), diffuse.a); +#endif +#endif + +#if 0 +#if defined(USE_VERTEX_SKINNING) + gl_FragColor = vec4(vec3(1.0, 0.0, 0.0), diffuse.a); +#elif defined(USE_VERTEX_ANIMATION) + gl_FragColor = vec4(vec3(0.0, 0.0, 1.0), diffuse.a); +#else + gl_FragColor = vec4(vec3(0.0, 1.0, 0.0), diffuse.a); +#endif +#endif +} diff --git a/main/glsl/forwardLighting_vp.glsl b/main/glsl/forwardLighting_vp.glsl new file mode 100644 index 0000000000..1e7b63a0d4 --- /dev/null +++ b/main/glsl/forwardLighting_vp.glsl @@ -0,0 +1,154 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* forwardLighting_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; +attribute vec3 attr_Tangent; +attribute vec3 attr_Binormal; +attribute vec3 attr_Normal; +attribute vec4 attr_Color; + +attribute vec4 attr_Position2; +attribute vec3 attr_Tangent2; +attribute vec3 attr_Binormal2; +attribute vec3 attr_Normal2; + +uniform float u_VertexInterpolation; + +uniform mat4 u_DiffuseTextureMatrix; +uniform mat4 u_NormalTextureMatrix; +uniform mat4 u_SpecularTextureMatrix; + +uniform vec4 u_ColorModulate; +uniform vec4 u_Color; + +uniform mat4 u_LightAttenuationMatrix; +uniform mat4 u_ModelMatrix; +uniform mat4 u_ModelViewProjectionMatrix; + +uniform float u_Time; + +varying vec3 var_Position; +varying vec4 var_TexDiffuse; +varying vec4 var_TexNormal; +#if defined(USE_NORMAL_MAPPING) +varying vec2 var_TexSpecular; +#endif + +varying vec4 var_TexAttenuation; + +#if defined(USE_NORMAL_MAPPING) +varying vec4 var_Tangent; +varying vec4 var_Binormal; +#endif +varying vec4 var_Normal; +//varying vec4 var_Color; // Tr3B - maximum vars reached + + + +void main() +{ + vec4 position; + vec3 tangent; + vec3 binormal; + vec3 normal; + +#if defined(USE_VERTEX_SKINNING) + + #if defined(USE_NORMAL_MAPPING) + VertexSkinning_P_TBN( attr_Position, attr_Tangent, attr_Binormal, attr_Normal, + position, tangent, binormal, normal); + #else + VertexSkinning_P_N( attr_Position, attr_Normal, + position, normal); + #endif + +#elif defined(USE_VERTEX_ANIMATION) + + #if defined(USE_NORMAL_MAPPING) + VertexAnimation_P_TBN( attr_Position, attr_Position2, + attr_Tangent, attr_Tangent2, + attr_Binormal, attr_Binormal2, + attr_Normal, attr_Normal2, + u_VertexInterpolation, + position, tangent, binormal, normal); + #else + VertexAnimation_P_N(attr_Position, attr_Position2, + attr_Normal, attr_Normal2, + u_VertexInterpolation, + position, normal); + #endif + +#else + position = attr_Position; + + #if defined(USE_NORMAL_MAPPING) + tangent = attr_Tangent; + binormal = attr_Binormal; + #endif + + normal = attr_Normal; +#endif + +#if defined(USE_DEFORM_VERTEXES) + position = DeformPosition2( position, + normal, + attr_TexCoord0.st, + u_Time); +#endif + + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * position; + + // transform position into world space + var_Position = (u_ModelMatrix * position).xyz; + +#if defined(USE_NORMAL_MAPPING) + var_Tangent.xyz = (u_ModelMatrix * vec4(tangent, 0.0)).xyz; + var_Binormal.xyz = (u_ModelMatrix * vec4(binormal, 0.0)).xyz; +#endif + + var_Normal.xyz = mat3(u_ModelMatrix) * normal; + + // calc light xy,z attenuation in light space + var_TexAttenuation = u_LightAttenuationMatrix * position; + + // transform diffusemap texcoords + var_TexDiffuse.xy = (u_DiffuseTextureMatrix * attr_TexCoord0).st; + +#if defined(USE_NORMAL_MAPPING) + // transform normalmap texcoords + var_TexNormal.xy = (u_NormalTextureMatrix * attr_TexCoord0).st; + + // transform specularmap texture coords + var_TexSpecular = (u_SpecularTextureMatrix * attr_TexCoord0).st; +#endif + + // assign color + vec4 color = attr_Color * u_ColorModulate + u_Color; + // color = vec4(1.0); + + var_TexDiffuse.p = color.r; + var_TexNormal.pq = color.gb; +} diff --git a/main/glsl/generic_fp.glsl b/main/glsl/generic_fp.glsl new file mode 100644 index 0000000000..f751c12dc5 --- /dev/null +++ b/main/glsl/generic_fp.glsl @@ -0,0 +1,72 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* generic_fp.glsl */ + +uniform sampler2D u_ColorMap; +uniform int u_AlphaTest; +uniform vec4 u_PortalPlane; + +varying vec3 var_Position; +varying vec2 var_Tex; +varying vec4 var_Color; + +void main() +{ +#if defined(USE_PORTAL_CLIPPING) + { + float dist = dot(var_Position.xyz, u_PortalPlane.xyz) - u_PortalPlane.w; + if(dist < 0.0) + { + discard; + return; + } + } +#endif + + vec4 color = texture2D(u_ColorMap, var_Tex); + +#if defined(USE_ALPHA_TESTING) + if(u_AlphaTest == ATEST_GT_0 && color.a <= 0.0) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_LT_128 && color.a >= 0.5) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_GE_128 && color.a < 0.5) + { + discard; + return; + } +#endif + + color *= var_Color; + gl_FragColor = color; + +#if 0 //defined(USE_TCGEN_ENVIRONMENT) + gl_FragColor = vec4(vec3(1.0, 0.0, 0.0), color.a); +#endif +} diff --git a/main/glsl/generic_vp.glsl b/main/glsl/generic_vp.glsl new file mode 100644 index 0000000000..a58e56e635 --- /dev/null +++ b/main/glsl/generic_vp.glsl @@ -0,0 +1,111 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* generic_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; +attribute vec3 attr_Normal; +attribute vec4 attr_Color; + +attribute vec4 attr_Position2; +attribute vec3 attr_Normal2; + +uniform float u_VertexInterpolation; + +uniform mat4 u_ColorTextureMatrix; +uniform vec3 u_ViewOrigin; +uniform int u_TCGen_Environment; + +uniform float u_Time; + +uniform vec4 u_ColorModulate; +uniform vec4 u_Color; +uniform mat4 u_ModelMatrix; +uniform mat4 u_ModelViewProjectionMatrix; + +varying vec3 var_Position; +varying vec2 var_Tex; +varying vec4 var_Color; + + + + + +void main() +{ + vec4 position; + vec3 normal; + +#if defined(USE_VERTEX_SKINNING) + + VertexSkinning_P_N( attr_Position, attr_Normal, + position, normal); + +#elif defined(USE_VERTEX_ANIMATION) + + VertexAnimation_P_N(attr_Position, attr_Position2, + attr_Normal, attr_Normal2, + u_VertexInterpolation, + position, normal); + +#else + position = attr_Position; + normal = attr_Normal; +#endif + +#if defined(USE_DEFORM_VERTEXES) + position = DeformPosition2( position, + normal, + attr_TexCoord0.st, + u_Time); +#endif + + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * position; + + // transform position into world space + var_Position = mat3(u_ModelMatrix) * position.xyz; + + // transform texcoords + vec4 texCoord; +#if defined(USE_TCGEN_ENVIRONMENT) + { + vec3 viewer = normalize(u_ViewOrigin - position.xyz); + + float d = dot(attr_Normal, viewer); + + vec3 reflected = attr_Normal * 2.0 * d - viewer; + + texCoord.s = 0.5 + reflected.y * 0.5; + texCoord.t = 0.5 - reflected.z * 0.5; + texCoord.q = 0; + texCoord.w = 1; + } +#else + texCoord = attr_TexCoord0; +#endif + + var_Tex = (u_ColorTextureMatrix * texCoord).st; + + var_Color = attr_Color * u_ColorModulate + u_Color; +} diff --git a/main/glsl/heatHaze_fp.glsl b/main/glsl/heatHaze_fp.glsl new file mode 100644 index 0000000000..6e72508223 --- /dev/null +++ b/main/glsl/heatHaze_fp.glsl @@ -0,0 +1,98 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* heatHaze_fp.glsl */ + +uniform sampler2D u_NormalMap; +uniform sampler2D u_CurrentMap; +#if defined(r_heatHazeFix) +uniform sampler2D u_ContrastMap; +#endif +uniform int u_AlphaTest; + +varying vec2 var_TexNormal; +varying float var_Deform; + +void main() +{ + vec4 color0, color1; + + // compute normal in tangent space from normalmap + color0 = texture2D(u_NormalMap, var_TexNormal).rgba; + vec3 N = 2.0 * (color0.rgb - 0.5); + + // calculate the screen texcoord in the 0.0 to 1.0 range + vec2 st = gl_FragCoord.st * r_FBufScale; + +#if defined(USE_ALPHA_TESTING) + if(u_AlphaTest == ATEST_GT_0 && color0.a <= 0.0) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_LT_128 && color0.a >= 0.5) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_GE_128 && color0.a < 0.5) + { + discard; + return; + } +#endif + + // offset by the scaled normal and clamp it to 0.0 - 1.0 + st += N.xy * var_Deform; + st = clamp(st, 0.0, 1.0); + + // scale by the screen non-power-of-two-adjust + st *= r_NPOTScale; + +#if defined(r_heatHazeFix) + // check if the distortion got too far + float vis = texture2D(u_ContrastMap, st).r; + if(vis > 0.0) + { + color0 = texture2D(u_CurrentMap, st); + color1 = vec4(0.0, 1.0, 0.0, color0.a); + } + else + { + // reset st and don't offset + st = gl_FragCoord.st * r_FBufScale * r_NPOTScale; + + color0 = texture2D(u_CurrentMap, st); + color1 = vec4(1.0, 0.0, 0.0, color0.a); + } + +#if 0 + gl_FragColor = texture2D(u_ContrastMap, gl_FragCoord.st * r_FBufScale * r_NPOTScale); + return; +#endif + +#else + color0 = texture2D(u_CurrentMap, st); +#endif + + gl_FragColor = color0; +} diff --git a/main/glsl/heatHaze_vp.glsl b/main/glsl/heatHaze_vp.glsl new file mode 100644 index 0000000000..a2e0fe16c9 --- /dev/null +++ b/main/glsl/heatHaze_vp.glsl @@ -0,0 +1,93 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* heatHaze_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec3 attr_Normal; +attribute vec4 attr_TexCoord0; + +attribute vec4 attr_Position2; +attribute vec3 attr_Normal2; + +uniform float u_VertexInterpolation; + +uniform float u_Time; + +uniform mat4 u_NormalTextureMatrix; +uniform mat4 u_ProjectionMatrixTranspose; +uniform mat4 u_ModelViewMatrixTranspose; +uniform mat4 u_ModelViewProjectionMatrix; + +uniform float u_DeformMagnitude; + +varying vec2 var_TexNormal; +varying float var_Deform; + +void main() +{ + vec4 deformVec; + float d1, d2; + + vec4 position; + vec3 normal; + +#if defined(USE_VERTEX_SKINNING) + + VertexSkinning_P_N( attr_Position, attr_Normal, + position, normal); + +#elif defined(USE_VERTEX_ANIMATION) + + VertexAnimation_P_N(attr_Position, attr_Position2, + attr_Normal, attr_Normal2, + u_VertexInterpolation, + position, normal); + +#else + position = attr_Position; + normal = attr_Normal; +#endif + +#if defined(USE_DEFORM_VERTEXES) + position = DeformPosition2( position, + normal, + attr_TexCoord0.st, + u_Time); +#endif + + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * position; + + // take the deform magnitude and scale it by the projection distance + deformVec = vec4(1, 0, 0, 1); + deformVec.z = dot(u_ModelViewMatrixTranspose[2], position); + + // transform normalmap texcoords + var_TexNormal = (u_NormalTextureMatrix * attr_TexCoord0).st; + + d1 = dot(u_ProjectionMatrixTranspose[0], deformVec); + d2 = dot(u_ProjectionMatrixTranspose[3], deformVec); + + // clamp the distance so the the deformations don't get too wacky near the view + var_Deform = min(d1 * (1.0 / max(d2, 1.0)), 0.02) * u_DeformMagnitude; +} diff --git a/main/glsl/lightMapping_fp.glsl b/main/glsl/lightMapping_fp.glsl new file mode 100644 index 0000000000..dc7f011dc3 --- /dev/null +++ b/main/glsl/lightMapping_fp.glsl @@ -0,0 +1,259 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* lightMapping_fp.glsl */ + +uniform sampler2D u_DiffuseMap; +uniform sampler2D u_NormalMap; +uniform sampler2D u_SpecularMap; +uniform sampler2D u_LightMap; +uniform sampler2D u_DeluxeMap; +uniform int u_AlphaTest; +uniform vec3 u_ViewOrigin; +uniform float u_DepthScale; +uniform vec4 u_PortalPlane; + +varying vec3 var_Position; +varying vec4 var_TexDiffuseNormal; +varying vec2 var_TexSpecular; +varying vec2 var_TexLight; + +varying vec3 var_Tangent; +varying vec3 var_Binormal; +varying vec3 var_Normal; + +varying vec4 var_Color; + + +void main() +{ +#if defined(USE_PORTAL_CLIPPING) + { + float dist = dot(var_Position.xyz, u_PortalPlane.xyz) - u_PortalPlane.w; + if(dist < 0.0) + { + discard; + return; + } + } +#endif + + +#if defined(USE_NORMAL_MAPPING) + + vec2 texDiffuse = var_TexDiffuseNormal.st; + vec2 texNormal = var_TexDiffuseNormal.pq; + vec2 texSpecular = var_TexSpecular.st; + + // invert tangent space for two sided surfaces + mat3 tangentToWorldMatrix; +#if defined(TWOSIDED) + if(gl_FrontFacing) + { + tangentToWorldMatrix = mat3(-var_Tangent.xyz, -var_Binormal.xyz, -var_Normal.xyz); + } + else +#endif + { + tangentToWorldMatrix = mat3(var_Tangent.xyz, var_Binormal.xyz, var_Normal.xyz); + } + + // compute view direction in world space + vec3 I = normalize(u_ViewOrigin - var_Position); + +#if defined(USE_PARALLAX_MAPPING) + // ray intersect in view direction + + mat3 worldToTangentMatrix; + #if defined(GLHW_ATI) || defined(GLHW_ATI_DX10) || defined(GLDRV_MESA) + worldToTangentMatrix = mat3(tangentToWorldMatrix[0][0], tangentToWorldMatrix[1][0], tangentToWorldMatrix[2][0], + tangentToWorldMatrix[0][1], tangentToWorldMatrix[1][1], tangentToWorldMatrix[2][1], + tangentToWorldMatrix[0][2], tangentToWorldMatrix[1][2], tangentToWorldMatrix[2][2]); + #else + worldToTangentMatrix = transpose(tangentToWorldMatrix); + #endif + + // compute view direction in tangent space + vec3 V = worldToTangentMatrix * (u_ViewOrigin - var_Position.xyz); + V = normalize(V); + + // size and start position of search in texture space + vec2 S = V.xy * -u_DepthScale / V.z; + +#if 0 + vec2 texOffset = vec2(0.0); + for(int i = 0; i < 4; i++) { + vec4 Normal = texture2D(u_NormalMap, texNormal.st + texOffset); + float height = Normal.a * 0.2 - 0.0125; + texOffset += height * Normal.z * S; + } +#else + float depth = RayIntersectDisplaceMap(texNormal, S, u_NormalMap); + + // compute texcoords offset + vec2 texOffset = S * depth; +#endif + + texDiffuse.st += texOffset; + texNormal.st += texOffset; + texSpecular.st += texOffset; +#endif // USE_PARALLAX_MAPPING + + // compute the diffuse term + vec4 diffuse = texture2D(u_DiffuseMap, texDiffuse); + +#if defined(USE_ALPHA_TESTING) + if(u_AlphaTest == ATEST_GT_0 && diffuse.a <= 0.0) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_LT_128 && diffuse.a >= 0.5) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_GE_128 && diffuse.a < 0.5) + { + discard; + return; + } +#endif + + + // compute normal in world space from normalmap + vec3 N = (2.0 * (texture2D(u_NormalMap, texNormal).xyz - 0.5)); + //N.x = -N.x; + //N = normalize(N); + //N = normalize(var_Normal.xyz); + N = tangentToWorldMatrix * N; + + // compute light direction in world space + vec3 L = 2.0 * (texture2D(u_DeluxeMap, var_TexLight).xyz - 0.5); + //L = normalize(L); + + // compute half angle in world space + vec3 H = normalize(L + I); + + // compute light color from world space lightmap + vec3 lightColor = texture2D(u_LightMap, var_TexLight).rgb; + + // compute the specular term + vec3 specular = texture2D(u_SpecularMap, texSpecular).rgb; + + float NdotL = clamp(dot(N, L), 0.0, 1.0); + + float NdotLnobump = clamp(dot(normalize(var_Normal.xyz), L), 0.004, 1.0); + //vec3 lightColorNoNdotL = clamp(lightColor.rgb / NdotLnobump, 0.0, 1.0); + + //float NdotLnobump = dot(normalize(var_Normal.xyz), L); + vec3 lightColorNoNdotL = lightColor.rgb / NdotLnobump; + + // compute final color + vec4 color = diffuse; + // color = vec4(vec3(1.0, 1.0, 1.0), diffuse.a); + //color.rgb = vec3(NdotLnobump, NdotLnobump, NdotLnobump); + //color.rgb *= lightColor.rgb; + //color.rgb = lightColorNoNdotL.rgb * NdotL; + color.rgb *= clamp(lightColorNoNdotL.rgb * NdotL, lightColor.rgb * 0.3, lightColor.rgb); + //color.rgb *= diffuse.rgb; + //color.rgb = L * 0.5 + 0.5; + color.rgb += specular * lightColorNoNdotL * pow(clamp(dot(N, H), 0.0, 1.0), r_SpecularExponent) * r_SpecularScale; + color.a = var_Color.a; // for terrain blending + + +#else // USE_NORMAL_MAPPING + + // compute the diffuse term + vec4 diffuse = texture2D(u_DiffuseMap, var_TexDiffuseNormal.st); + +#if defined(USE_ALPHA_TESTING) + if(u_AlphaTest == ATEST_GT_0 && diffuse.a <= 0.0) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_LT_128 && diffuse.a >= 0.5) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_GE_128 && diffuse.a < 0.5) + { + discard; + return; + } +#endif + + vec3 N; + +#if defined(TWOSIDED) + if(gl_FrontFacing) + { + N = -normalize(var_Normal); + } + else +#endif + { + N = normalize(var_Normal); + } + + vec3 specular = vec3(0.0, 0.0, 0.0); + + // compute light color from object space lightmap + vec3 lightColor = texture2D(u_LightMap, var_TexLight).rgb; + + vec4 color = diffuse; + color.rgb *= lightColor; + color.a = var_Color.a; // for terrain blending +#endif + + // convert normal to [0,1] color space + N = N * 0.5 + 0.5; + +#if defined(r_DeferredShading) + gl_FragData[0] = color; // var_Color; + gl_FragData[1] = vec4(diffuse.rgb, var_Color.a); // vec4(var_Color.rgb, 1.0 - var_Color.a); + gl_FragData[2] = vec4(N, var_Color.a); + gl_FragData[3] = vec4(specular, var_Color.a); +#else + gl_FragColor = color; +#endif + +#if defined(r_showLightMaps) + gl_FragColor = texture2D(u_LightMap, var_TexLight); +#elif defined(r_showDeluxeMaps) + gl_FragColor = texture2D(u_DeluxeMap, var_TexLight); +#endif + + +#if 0 +#if defined(USE_PARALLAX_MAPPING) + gl_FragColor = vec4(vec3(1.0, 0.0, 0.0), diffuse.a); +#elif defined(USE_NORMAL_MAPPING) + gl_FragColor = vec4(vec3(0.0, 0.0, 1.0), diffuse.a); +#else + gl_FragColor = vec4(vec3(0.0, 1.0, 0.0), diffuse.a); +#endif +#endif + +} diff --git a/main/glsl/lightMapping_vp.glsl b/main/glsl/lightMapping_vp.glsl new file mode 100644 index 0000000000..dcca2b917f --- /dev/null +++ b/main/glsl/lightMapping_vp.glsl @@ -0,0 +1,114 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* lightMapping_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; +attribute vec4 attr_TexCoord1; +attribute vec3 attr_Tangent; +attribute vec3 attr_Binormal; +attribute vec3 attr_Normal; +attribute vec4 attr_Color; + +uniform mat4 u_DiffuseTextureMatrix; +uniform mat4 u_NormalTextureMatrix; +uniform mat4 u_SpecularTextureMatrix; +uniform mat4 u_ModelMatrix; +uniform mat4 u_ModelViewProjectionMatrix; + +uniform float u_Time; + +uniform vec4 u_ColorModulate; +uniform vec4 u_Color; + +varying vec3 var_Position; +varying vec4 var_TexDiffuseNormal; +varying vec2 var_TexSpecular; +varying vec2 var_TexLight; +varying vec3 var_Tangent; +varying vec3 var_Binormal; +varying vec3 var_Normal; +varying vec4 var_Color; + + + +void main() +{ + vec4 position = attr_Position; + +#if defined(USE_DEFORM_VERTEXES) + position = DeformPosition2( position, + attr_Normal, + attr_TexCoord0.st, + u_Time); +#endif + + // transform vertex position into homogenous clip-space +#if 1 + gl_Position = u_ModelViewProjectionMatrix * position; +#else + gl_Position.xy = attr_TexCoord1 * 2.0 - 1.0; + gl_Position.z = 0.0; + gl_Position.w = 1.0; +#endif + + + // transform diffusemap texcoords + var_TexDiffuseNormal.st = (u_DiffuseTextureMatrix * attr_TexCoord0).st; + var_TexLight = attr_TexCoord1.st; + +#if defined(USE_NORMAL_MAPPING) + // transform normalmap texcoords + var_TexDiffuseNormal.pq = (u_NormalTextureMatrix * attr_TexCoord0).st; + + // transform specularmap texcoords + var_TexSpecular = (u_SpecularTextureMatrix * attr_TexCoord0).st; +#endif + + +#if 0 + + // transform position into world space + var_Position = (u_ModelMatrix * position).xyz; + + var_Normal.xyz = (u_ModelMatrix * vec4(attr_Normal, 0.0)).xyz; + +#if defined(USE_NORMAL_MAPPING) + var_Tangent.xyz = (u_ModelMatrix * vec4(attr_Tangent, 0.0)).xyz; + var_Binormal.xyz = (u_ModelMatrix * vec4(attr_Binormal, 0.0)).xyz; +#endif + +#else + + var_Position = position.xyz; + var_Normal = attr_Normal.xyz; + +#if defined(USE_NORMAL_MAPPING) + var_Tangent = attr_Tangent.xyz; + var_Binormal = attr_Binormal.xyz; +#endif + +#endif + + var_Color = attr_Color * u_ColorModulate + u_Color; +} diff --git a/main/glsl/lightVolume_omni_fp.glsl b/main/glsl/lightVolume_omni_fp.glsl new file mode 100644 index 0000000000..e1b9d7c098 --- /dev/null +++ b/main/glsl/lightVolume_omni_fp.glsl @@ -0,0 +1,151 @@ +/* +=========================================================================== +Copyright (C) 2007-2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* lightVolume_omni_fp.glsl */ + +uniform sampler2D u_DepthMap; +uniform sampler2D u_AttenuationMapXY; +uniform sampler2D u_AttenuationMapZ; +uniform samplerCube u_ShadowMap; +uniform vec3 u_ViewOrigin; +uniform vec3 u_LightOrigin; +uniform vec3 u_LightColor; +uniform float u_LightRadius; +uniform float u_LightScale; +uniform mat4 u_LightAttenuationMatrix; +uniform int u_ShadowCompare; +uniform mat4 u_UnprojectMatrix; + +varying vec2 var_TexDiffuse; +varying vec3 var_TexAttenXYZ; + +void main() +{ + // calculate the screen texcoord in the 0.0 to 1.0 range + vec2 st = gl_FragCoord.st * r_FBufScale; + + // scale by the screen non-power-of-two-adjust + st *= r_NPOTScale; + + // reconstruct vertex position in world space + float depth = texture2D(u_DepthMap, st).r; + vec4 P = u_UnprojectMatrix * vec4(gl_FragCoord.xy, depth, 1.0); + P.xyz /= P.w; + +#if 0 + if(bool(u_PortalClipping)) + { + float dist = dot(P.xyz, u_PortalPlane.xyz) - u_PortalPlane.w; + if(dist < 0.0) + { + discard; + return; + } + } +#endif + + // compute incident ray in world space + vec3 R = normalize(P.xyz - u_ViewOrigin); + //vec3 R = normalize(u_ViewOrigin - P.xyz); + + //float traceDistance = dot(P.xyz - (u_ViewOrigin.xyz + R * u_ZNear ), forward); + //traceDistance = clamp(traceDistance, 0.0, 2500.0 ); // Far trace distance + + float traceDistance = distance(P.xyz, u_ViewOrigin); + traceDistance = clamp(traceDistance, 0.0, 2500.0); + + // TODO move to front clipping plane + + vec4 color = vec4(0.0, 0.0, 0.0, 1.0); + + //int steps = 40; + //float stepSize = traceDistance / float(steps); + + int steps = int(min(traceDistance, 2000.0)); // TODO r_MaxSteps + float stepSize = 64.0; + + for(float tracedDistance = 0.0; tracedDistance < traceDistance; tracedDistance += stepSize) + { + //vec3 T = P.xyz + (R * stepSize * float(i)); + //vec3 T = u_ViewOrigin + (R * stepSize * float(i)); + vec3 T = u_ViewOrigin + (R * tracedDistance); + + // compute attenuation + vec3 texAttenXYZ = (u_LightAttenuationMatrix * vec4(T, 1.0)).xyz; + vec3 attenuationXY = texture2D(u_AttenuationMapXY, texAttenXYZ.xy).rgb; + vec3 attenuationZ = texture2D(u_AttenuationMapZ, vec2(texAttenXYZ.z, 0)).rgb; + + float shadow = 1.0; + + #if defined(VSM) + if(bool(u_ShadowCompare)) + { + // compute incident ray + vec3 I2 = T - u_LightOrigin; + + vec4 shadowMoments = textureCube(u_ShadowMap, I2); + + #if defined(VSM_CLAMP) + // convert to [-1, 1] vector space + shadowMoments = 0.5 * (shadowMoments + 1.0); + #endif + + float shadowDistance = shadowMoments.r; + float shadowDistanceSquared = shadowMoments.g; + + const float SHADOW_BIAS = 0.001; + float vertexDistance = length(I2) / u_LightRadius - SHADOW_BIAS; + + // standard shadow map comparison + shadow = vertexDistance <= shadowDistance ? 1.0 : 0.0; + + // variance shadow mapping + float E_x2 = shadowDistanceSquared; + float Ex_2 = shadowDistance * shadowDistance; + + // AndyTX: VSM_EPSILON is there to avoid some ugly numeric instability with fp16 + float variance = min(max(E_x2 - Ex_2, 0.0) + VSM_EPSILON, 1.0); + + float mD = shadowDistance - vertexDistance; + float mD_2 = mD * mD; + float p = variance / (variance + mD_2); + + color.rgb += attenuationXY * attenuationZ * max(shadow, p); + } + + if(shadow <= 0.0) + { + continue; + } + else + #endif + { + color.rgb += attenuationXY * attenuationZ; + } + } + + color.rgb /= float(steps); + color.rgb *= u_LightColor; + //color.rgb *= u_LightScale; + + gl_FragColor = color; +} diff --git a/main/glsl/lightVolume_omni_vp.glsl b/main/glsl/lightVolume_omni_vp.glsl new file mode 100644 index 0000000000..0271ceafd9 --- /dev/null +++ b/main/glsl/lightVolume_omni_vp.glsl @@ -0,0 +1,33 @@ +/* +=========================================================================== +Copyright (C) 2007-2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* lightVolume_omni_vp.glsl */ + +attribute vec4 attr_Position; + +uniform mat4 u_ModelViewProjectionMatrix; + +void main() +{ + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * attr_Position; +} diff --git a/main/glsl/liquid_fp.glsl b/main/glsl/liquid_fp.glsl new file mode 100644 index 0000000000..5215de14f2 --- /dev/null +++ b/main/glsl/liquid_fp.glsl @@ -0,0 +1,200 @@ +/* +=========================================================================== +Copyright (C) 2007-2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* liquid_fp.glsl */ + +uniform sampler2D u_CurrentMap; +uniform sampler2D u_PortalMap; +uniform sampler2D u_DepthMap; +uniform sampler2D u_NormalMap; +uniform vec3 u_ViewOrigin; +uniform float u_FogDensity; +uniform vec3 u_FogColor; +uniform float u_RefractionIndex; +uniform float u_FresnelPower; +uniform float u_FresnelScale; +uniform float u_FresnelBias; +uniform float u_NormalScale; +uniform mat4 u_ModelMatrix; +uniform mat4 u_UnprojectMatrix; + +varying vec3 var_Position; +varying vec2 var_TexNormal; +varying vec3 var_Tangent; +varying vec3 var_Binormal; +varying vec3 var_Normal; +varying vec4 var_LightColor; +varying vec3 var_LightDirection; + +#if defined(r_ParallaxMapping) +float RayIntersectDisplaceMap(vec2 dp, vec2 ds) +{ + const int linearSearchSteps = 16; + const int binarySearchSteps = 6; + + float depthStep = 1.0 / float(linearSearchSteps); + + // current size of search window + float size = depthStep; + + // current depth position + float depth = 0.0; + + // best match found (starts with last position 1.0) + float bestDepth = 1.0; + + // search front to back for first point inside object + for(int i = 0; i < linearSearchSteps - 1; ++i) + { + depth += size; + + vec4 t = texture2D(u_NormalMap, dp + ds * depth); + + if(bestDepth > 0.996) // if no depth found yet + if(depth >= t.w) + bestDepth = depth; // store best depth + } + + depth = bestDepth; + + // recurse around first point (depth) for closest match + for(int i = 0; i < binarySearchSteps; ++i) + { + size *= 0.5; + + vec4 t = texture2D(u_NormalMap, dp + ds * depth); + + if(depth >= t.w) + #ifdef RM_DOUBLEDEPTH + if(depth <= t.z) + #endif + { + bestDepth = depth; + depth -= 2.0 * size; + } + + depth += size; + } + + return bestDepth; +} +#endif + + +void main() +{ + // compute incident ray + vec3 I = normalize(u_ViewOrigin - var_Position); + + mat3 tangentToWorldMatrix; + if(gl_FrontFacing) + tangentToWorldMatrix = mat3(-var_Tangent.xyz, -var_Binormal.xyz, -var_Normal.xyz); + else + tangentToWorldMatrix = mat3(var_Tangent.xyz, var_Binormal.xyz, var_Normal.xyz); + + mat3 worldToTangentMatrix; +#if defined(GLHW_ATI) || defined(GLHW_ATI_DX10) || defined(GLDRV_MESA) + worldToTangentMatrix = mat3(tangentToWorldMatrix[0][0], tangentToWorldMatrix[1][0], tangentToWorldMatrix[2][0], + tangentToWorldMatrix[0][1], tangentToWorldMatrix[1][1], tangentToWorldMatrix[2][1], + tangentToWorldMatrix[0][2], tangentToWorldMatrix[1][2], tangentToWorldMatrix[2][2]); +#else + worldToTangentMatrix = transpose(tangentToWorldMatrix); +#endif + + // calculate the screen texcoord in the 0.0 to 1.0 range + vec2 texScreen = gl_FragCoord.st * r_FBufScale * r_NPOTScale; + vec2 texNormal = var_TexNormal.st; + +#if defined(r_ParallaxMapping) + // compute view direction in tangent space + vec3 V = worldToTangentMatrix * (I); + V = normalize(V); + + // ray intersect in view direction + + // size and start position of search in texture space + //vec2 S = V.xy * -u_DepthScale / V.z; + vec2 S = V.xy * -0.03 / V.z; + + float depth = RayIntersectDisplaceMap(texNormal, S); + + // compute texcoords offset + vec2 texOffset = S * depth; + + texScreen.st += texOffset; + texNormal.st += texOffset; +#endif + + // compute normals + + vec3 N = normalize(var_Normal); + + vec3 N2 = 2.0 * (texture2D(u_NormalMap, texNormal).xyz - 0.5); + N2 = tangentToWorldMatrix * N2; + + // compute fresnel term + float fresnel = clamp(u_FresnelBias + pow(1.0 - dot(I, N), u_FresnelPower) * + u_FresnelScale, 0.0, 1.0); + + texScreen += u_NormalScale * N2.xy; + + vec3 refractColor = texture2D(u_CurrentMap, texScreen).rgb; + vec3 reflectColor = texture2D(u_PortalMap, texScreen).rgb; + + vec4 color; + + color.rgb = mix(refractColor, reflectColor, fresnel); + color.a = 1.0; + + if(u_FogDensity > 0.0) + { + // reconstruct vertex position in world space + float depth = texture2D(u_DepthMap, texScreen).r; + vec4 P = u_UnprojectMatrix * vec4(gl_FragCoord.xy, depth, 1.0); + P.xyz /= P.w; + + // calculate fog distance + float fogDistance = distance(P.xyz, var_Position); + + // calculate fog exponent + float fogExponent = fogDistance * u_FogDensity; + + // calculate fog factor + float fogFactor = exp2(-abs(fogExponent)); + + color.rgb = mix(u_FogColor, color.rgb, fogFactor); + } + + vec3 L = normalize(var_LightDirection); + + // compute half angle in world space + vec3 H = normalize(L + I); + + // compute the light term + vec3 light = var_LightColor.rgb * clamp(dot(N2, L), 0.0, 1.0); + + // compute the specular term + vec3 specular = reflectColor * var_LightColor.rgb * pow(clamp(dot(N2, H), 0.0, 1.0), r_SpecularExponent) * r_SpecularScale; + color.rgb += specular; + + gl_FragColor = color; +} diff --git a/main/glsl/liquid_vp.glsl b/main/glsl/liquid_vp.glsl new file mode 100644 index 0000000000..dfa45af7c3 --- /dev/null +++ b/main/glsl/liquid_vp.glsl @@ -0,0 +1,65 @@ +/* +=========================================================================== +Copyright (C) 2007-2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* liquid_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; +attribute vec3 attr_Tangent; +attribute vec3 attr_Binormal; +attribute vec3 attr_Normal; +attribute vec4 attr_Color; +attribute vec3 attr_LightDirection; + +uniform mat4 u_NormalTextureMatrix; +uniform mat4 u_ModelMatrix; +uniform mat4 u_ModelViewProjectionMatrix; + +varying vec3 var_Position; +varying vec2 var_TexNormal; +varying vec3 var_Tangent; +varying vec3 var_Binormal; +varying vec3 var_Normal; +varying vec4 var_LightColor; +varying vec3 var_LightDirection; + +void main() +{ + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * attr_Position; + + // transform position into world space + var_Position = (u_ModelMatrix * attr_Position).xyz; + + // transform normalmap texcoords + var_TexNormal = (u_NormalTextureMatrix * attr_TexCoord0).st; + + var_Tangent.xyz = (u_ModelMatrix * vec4(attr_Tangent, 0.0)).xyz; + var_Binormal.xyz = (u_ModelMatrix * vec4(attr_Binormal, 0.0)).xyz; + + // transform normal into world space + var_Normal = (u_ModelMatrix * vec4(attr_Normal, 0.0)).xyz; + + var_LightColor = attr_Color; + var_LightDirection = attr_LightDirection; +} + diff --git a/main/glsl/portal_fp.glsl b/main/glsl/portal_fp.glsl new file mode 100644 index 0000000000..c3bde495ac --- /dev/null +++ b/main/glsl/portal_fp.glsl @@ -0,0 +1,48 @@ +/* +=========================================================================== +Copyright (C) 2008 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* portal_fp.glsl */ + +uniform sampler2D u_CurrentMap; +uniform float u_PortalRange; + +varying vec3 var_Position; +varying vec4 var_Color; + +void main() +{ + // calculate the screen texcoord in the 0.0 to 1.0 range + vec2 st = gl_FragCoord.st * r_FBufScale; + + // scale by the screen non-power-of-two-adjust + st *= r_NPOTScale; + + vec4 color = texture2D(u_CurrentMap, st); + color *= var_Color; + + float len = length(var_Position); + + len /= u_PortalRange; + color.rgb *= 1.0 - clamp(len, 0.0, 1.0); + + gl_FragColor = color; +} diff --git a/main/glsl/portal_vp.glsl b/main/glsl/portal_vp.glsl new file mode 100644 index 0000000000..87a79a7143 --- /dev/null +++ b/main/glsl/portal_vp.glsl @@ -0,0 +1,44 @@ +/* +=========================================================================== +Copyright (C) 2008 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* portal_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec4 attr_Color; + +uniform mat4 u_ModelViewMatrix; +uniform mat4 u_ModelViewProjectionMatrix; + +varying vec3 var_Position; +varying vec4 var_Color; + +void main() +{ + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * attr_Position; + + // transform vertex position into camera space + var_Position = (u_ModelViewMatrix * attr_Position).xyz; + + // assign color + var_Color = attr_Color; +} diff --git a/main/glsl/reflection_CB_fp.glsl b/main/glsl/reflection_CB_fp.glsl new file mode 100644 index 0000000000..c4e560beed --- /dev/null +++ b/main/glsl/reflection_CB_fp.glsl @@ -0,0 +1,72 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* reflection_CB_fp.glsl */ + +uniform samplerCube u_ColorMap; +uniform sampler2D u_NormalMap; +uniform vec3 u_ViewOrigin; +uniform mat4 u_ModelMatrix; + +varying vec3 var_Position; +varying vec2 var_TexNormal; +varying vec4 var_Tangent; +varying vec4 var_Binormal; +varying vec4 var_Normal; + +void main() +{ + // compute incident ray in world space + vec3 I = normalize(var_Position - u_ViewOrigin); + + +#if defined(USE_NORMAL_MAPPING) + // compute normal in tangent space from normalmap + vec3 N = 2.0 * (texture2D(u_NormalMap, var_TexNormal.st).xyz - 0.5); + #if defined(r_NormalScale) + N.z *= r_NormalScale; + normalize(N); + #endif + + // invert tangent space for twosided surfaces + mat3 tangentToWorldMatrix; +#if defined(TWOSIDED) + if(gl_FrontFacing) + tangentToWorldMatrix = mat3(-var_Tangent.xyz, -var_Binormal.xyz, -var_Normal.xyz); + else +#endif + tangentToWorldMatrix = mat3(var_Tangent.xyz, var_Binormal.xyz, var_Normal.xyz); + + // transform normal into world space + N = tangentToWorldMatrix * N; + +#else + + vec3 N = normalize(var_Normal.xyz); +#endif + + // compute reflection ray + vec3 R = reflect(I, N); + + gl_FragColor = textureCube(u_ColorMap, R).rgba; + // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); +} diff --git a/main/glsl/reflection_CB_vp.glsl b/main/glsl/reflection_CB_vp.glsl new file mode 100644 index 0000000000..02206c324a --- /dev/null +++ b/main/glsl/reflection_CB_vp.glsl @@ -0,0 +1,118 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* reflection_CB_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; +attribute vec3 attr_Tangent; +attribute vec3 attr_Binormal; +attribute vec3 attr_Normal; + +attribute vec4 attr_Position2; +attribute vec3 attr_Tangent2; +attribute vec3 attr_Binormal2; +attribute vec3 attr_Normal2; + +uniform float u_VertexInterpolation; +uniform mat4 u_NormalTextureMatrix; +uniform mat4 u_ModelMatrix; +uniform mat4 u_ModelViewProjectionMatrix; + +uniform float u_Time; + +varying vec3 var_Position; +varying vec2 var_TexNormal; +varying vec4 var_Tangent; +varying vec4 var_Binormal; +varying vec4 var_Normal; + +void main() +{ + vec4 position; + vec3 tangent; + vec3 binormal; + vec3 normal; + +#if defined(USE_VERTEX_SKINNING) + + #if defined(USE_NORMAL_MAPPING) + VertexSkinning_P_TBN( attr_Position, attr_Tangent, attr_Binormal, attr_Normal, + position, tangent, binormal, normal); + #else + VertexSkinning_P_N( attr_Position, attr_Normal, + position, normal); + #endif + +#elif defined(USE_VERTEX_ANIMATION) + + #if defined(USE_NORMAL_MAPPING) + VertexAnimation_P_TBN( attr_Position, attr_Position2, + attr_Tangent, attr_Tangent2, + attr_Binormal, attr_Binormal2, + attr_Normal, attr_Normal2, + u_VertexInterpolation, + position, tangent, binormal, normal); + #else + VertexAnimation_P_N(attr_Position, attr_Position2, + attr_Normal, attr_Normal2, + u_VertexInterpolation, + position, normal); + #endif + +#else + position = attr_Position; + + #if defined(USE_NORMAL_MAPPING) + tangent = attr_Tangent; + binormal = attr_Binormal; + #endif + + normal = attr_Normal; +#endif + +#if defined(USE_DEFORM_VERTEXES) + position = DeformPosition2( position, + normal, + attr_TexCoord0.st, + u_Time); +#endif + + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * position; + + // transform position into world space + var_Position = (u_ModelMatrix * position).xyz; + + #if defined(USE_NORMAL_MAPPING) + var_Tangent.xyz = (u_ModelMatrix * vec4(tangent, 0.0)).xyz; + var_Binormal.xyz = (u_ModelMatrix * vec4(binormal, 0.0)).xyz; + #endif + + var_Normal.xyz = (u_ModelMatrix * vec4(normal, 0.0)).xyz; + +#if defined(USE_NORMAL_MAPPING) + // transform normalmap texcoords + var_TexNormal = (u_NormalTextureMatrix * attr_TexCoord0).st; +#endif +} + diff --git a/main/glsl/refraction_C_fp.glsl b/main/glsl/refraction_C_fp.glsl new file mode 100644 index 0000000000..e4ab80991c --- /dev/null +++ b/main/glsl/refraction_C_fp.glsl @@ -0,0 +1,63 @@ +/* +=========================================================================== +Copyright (C) 2006 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* refraction_C_fp.glsl */ + +uniform samplerCube u_ColorMap; +uniform vec3 u_ViewOrigin; +uniform float u_RefractionIndex; +uniform float u_FresnelPower; +uniform float u_FresnelScale; +uniform float u_FresnelBias; + +varying vec3 var_Position; +varying vec3 var_Normal; + +void main() +{ + // compute incident ray + vec3 I = normalize(var_Position - u_ViewOrigin); + + // compute normal + vec3 N = normalize(var_Normal); + + // compute reflection ray + vec3 R = reflect(I, N); + + // compute refraction ray + vec3 T = refract(I, N, u_RefractionIndex); + + // compute fresnel term + float fresnel = u_FresnelBias + pow(1.0 - dot(I, N), u_FresnelPower) * u_FresnelScale; + + vec3 reflectColor = textureCube(u_ColorMap, R).rgb; + vec3 refractColor = textureCube(u_ColorMap, T).rgb; + + // compute final color + vec4 color; + color.r = (1.0 - fresnel) * refractColor.r + reflectColor.r * fresnel; + color.g = (1.0 - fresnel) * refractColor.g + reflectColor.g * fresnel; + color.b = (1.0 - fresnel) * refractColor.b + reflectColor.b * fresnel; + color.a = 1.0; + + gl_FragColor = color; +} diff --git a/main/glsl/refraction_C_vp.glsl b/main/glsl/refraction_C_vp.glsl new file mode 100644 index 0000000000..e70f966d06 --- /dev/null +++ b/main/glsl/refraction_C_vp.glsl @@ -0,0 +1,80 @@ +/* +=========================================================================== +Copyright (C) 2006-2008 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* refraction_C_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec3 attr_Normal; +#if defined(r_VertexSkinning) +attribute vec4 attr_BoneIndexes; +attribute vec4 attr_BoneWeights; +uniform int u_VertexSkinning; +uniform mat4 u_BoneMatrix[MAX_GLSL_BONES]; +#endif + +uniform mat4 u_ModelMatrix; +uniform mat4 u_ModelViewProjectionMatrix; + +varying vec3 var_Position; +varying vec3 var_Normal; + +void main() +{ +#if defined(r_VertexSkinning) + if(bool(u_VertexSkinning)) + { + vec4 vertex = vec4(0.0); + vec3 normal = vec3(0.0); + + for(int i = 0; i < 4; i++) + { + int boneIndex = int(attr_BoneIndexes[i]); + float boneWeight = attr_BoneWeights[i]; + mat4 boneMatrix = u_BoneMatrix[boneIndex]; + + vertex += (boneMatrix * attr_Position) * boneWeight; + normal += (boneMatrix * vec4(attr_Normal, 0.0)).xyz * boneWeight; + } + + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * vertex; + + // transform position into world space + var_Position = (u_ModelMatrix * vertex).xyz; + + // transform normal into world space + var_Normal = (u_ModelMatrix * vec4(normal, 0.0)).xyz; + } + else +#endif + { + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * attr_Position; + + // transform position into world space + var_Position = (u_ModelMatrix * attr_Position).xyz; + + // transform normal into world space + var_Normal = (u_ModelMatrix * vec4(attr_Normal, 0.0)).xyz; + } +} + diff --git a/main/glsl/reliefMapping_fp.glsl b/main/glsl/reliefMapping_fp.glsl new file mode 100644 index 0000000000..da3d494db9 --- /dev/null +++ b/main/glsl/reliefMapping_fp.glsl @@ -0,0 +1,79 @@ +/* +=========================================================================== +Copyright (C) 2009-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// reliefMapping_vp.glsl - Relief mapping helper functions + +#if defined(USE_PARALLAX_MAPPING) +float RayIntersectDisplaceMap(vec2 dp, vec2 ds, sampler2D normalMap) +{ + const int linearSearchSteps = 16; + const int binarySearchSteps = 6; + + float depthStep = 1.0 / float(linearSearchSteps); + + // current size of search window + float size = depthStep; + + // current depth position + float depth = 0.0; + + // best match found (starts with last position 1.0) + float bestDepth = 1.0; + + // search front to back for first point inside object + for(int i = 0; i < linearSearchSteps - 1; ++i) + { + depth += size; + + vec4 t = texture2D(normalMap, dp + ds * depth); + + if(bestDepth > 0.996) // if no depth found yet + if(depth >= t.w) + bestDepth = depth; // store best depth + } + + depth = bestDepth; + + // recurse around first point (depth) for closest match + for(int i = 0; i < binarySearchSteps; ++i) + { + size *= 0.5; + + vec4 t = texture2D(normalMap, dp + ds * depth); + + if(depth >= t.w) + #ifdef RM_DOUBLEDEPTH + if(depth <= t.z) + #endif + { + bestDepth = depth; + depth -= 2.0 * size; + } + + depth += size; + } + + return bestDepth; +} +#endif + + + diff --git a/main/glsl/screen_fp.glsl b/main/glsl/screen_fp.glsl new file mode 100644 index 0000000000..40822c1e88 --- /dev/null +++ b/main/glsl/screen_fp.glsl @@ -0,0 +1,41 @@ +/* +=========================================================================== +Copyright (C) 2006 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* screen_fp.glsl */ + +uniform sampler2D u_CurrentMap; + +varying vec4 var_Color; + +void main() +{ + // calculate the screen texcoord in the 0.0 to 1.0 range + vec2 st = gl_FragCoord.st * r_FBufScale; + + // scale by the screen non-power-of-two-adjust + st *= r_NPOTScale; + + vec4 color = texture2D(u_CurrentMap, st); + color *= var_Color; + + gl_FragColor = color; +} diff --git a/main/glsl/screen_vp.glsl b/main/glsl/screen_vp.glsl new file mode 100644 index 0000000000..b8b54a8c36 --- /dev/null +++ b/main/glsl/screen_vp.glsl @@ -0,0 +1,39 @@ +/* +=========================================================================== +Copyright (C) 2006-2008 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* screen_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec4 attr_Color; + +uniform mat4 u_ModelViewProjectionMatrix; + +varying vec4 var_Color; + +void main() +{ + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * attr_Position; + + // assign color + var_Color = attr_Color; +} diff --git a/main/glsl/shadowFill_fp.glsl b/main/glsl/shadowFill_fp.glsl new file mode 100644 index 0000000000..b5c786acab --- /dev/null +++ b/main/glsl/shadowFill_fp.glsl @@ -0,0 +1,140 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* shadowFill_fp.glsl */ + +uniform sampler2D u_ColorMap; +uniform int u_AlphaTest; +uniform vec4 u_PortalPlane; +uniform vec3 u_LightOrigin; +uniform float u_LightRadius; + +varying vec3 var_Position; +varying vec2 var_Tex; +varying vec4 var_Color; + + +#if defined(EVSM) + +vec2 WarpDepth(float depth) +{ + // rescale depth into [-1, 1] + depth = 2.0 * depth - 1.0; + float pos = exp( r_EVSMExponents.x * depth); + float neg = -exp(-r_EVSMExponents.y * depth); + + return vec2(pos, neg); +} + +vec4 ShadowDepthToEVSM(float depth) +{ + vec2 warpedDepth = WarpDepth(depth); + return vec4(warpedDepth.xy, warpedDepth.xy * warpedDepth.xy); +} + +#endif // #if defined(EVSM) + +void main() +{ +#if defined(USE_PORTAL_CLIPPING) + { + float dist = dot(var_Position.xyz, u_PortalPlane.xyz) - u_PortalPlane.w; + if(dist < 0.0) + { + discard; + return; + } + } +#endif + + vec4 color = texture2D(u_ColorMap, var_Tex); + +#if defined(USE_ALPHA_TESTING) + if(u_AlphaTest == ATEST_GT_0 && color.a <= 0.0) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_LT_128 && color.a >= 0.5) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_GE_128 && color.a < 0.5) + { + discard; + return; + } +#endif + + +#if defined(VSM) + + float distance; + +#if defined(LIGHT_DIRECTIONAL) + distance = gl_FragCoord.z; +#else + distance = length(var_Position - u_LightOrigin) / u_LightRadius; +#endif + + float distanceSquared = distance * distance; + + // shadowmap can be float RGBA or luminance alpha so store distanceSquared into alpha + +#if defined(VSM_CLAMP) + // convert to [0,1] color space + gl_FragColor = vec4(distance, 0.0 , 0.0, distanceSquared) * 0.5 + 0.5; +#else + gl_FragColor = vec4(distance, 0.0, 0.0, distanceSquared); +#endif + +#elif defined(EVSM) || defined(ESM) + + float distance; +#if defined(LIGHT_DIRECTIONAL) + { + distance = gl_FragCoord.z;// * r_ShadowMapDepthScale; + //distance /= gl_FragCoord.w; + //distance = var_Position.z / var_Position.w; + //distance = var_Position.z; + } +#else + { + distance = (length(var_Position - u_LightOrigin) / u_LightRadius); // * r_ShadowMapDepthScale; + } +#endif + +#if defined(EVSM) +#if !defined(r_EVSMPostProcess) + gl_FragColor = ShadowDepthToEVSM(distance); +#else + gl_FragColor = vec4(0.0, 0.0, 0.0, distance); +#endif +#else + gl_FragColor = vec4(0.0, 0.0, 0.0, distance); +#endif // defined(EVSM) + +#else + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); +#endif +} diff --git a/main/glsl/shadowFill_vp.glsl b/main/glsl/shadowFill_vp.glsl new file mode 100644 index 0000000000..93d5fea74b --- /dev/null +++ b/main/glsl/shadowFill_vp.glsl @@ -0,0 +1,93 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* shadowFill_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec3 attr_Normal; +attribute vec4 attr_TexCoord0; +attribute vec4 attr_Color; + +attribute vec4 attr_Position2; +attribute vec3 attr_Normal2; + +uniform float u_VertexInterpolation; + +uniform vec4 u_Color; + +uniform mat4 u_ColorTextureMatrix; +uniform mat4 u_ModelMatrix; +uniform mat4 u_ModelViewProjectionMatrix; + +uniform float u_Time; + +varying vec3 var_Position; +varying vec2 var_Tex; +varying vec4 var_Color; + + + +void main() +{ + vec4 position; + vec3 normal; + +#if defined(USE_VERTEX_SKINNING) + + VertexSkinning_P_N( attr_Position, attr_Normal, + position, normal); + +#elif defined(USE_VERTEX_ANIMATION) + + VertexAnimation_P_N(attr_Position, attr_Position2, + attr_Normal, attr_Normal2, + u_VertexInterpolation, + position, normal); + +#else + position = attr_Position; + normal = attr_Normal; +#endif + +#if defined(USE_DEFORM_VERTEXES) + position = DeformPosition2( position, + normal, + attr_TexCoord0.st, + u_Time); +#endif + + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * position; + // transform position into world space +#if defined(LIGHT_DIRECTIONAL) + var_Position = gl_Position.xyz / gl_Position.w; +#else + // transform position into world space + var_Position = (u_ModelMatrix * position).xyz; +#endif + + // transform texcoords + var_Tex = (u_ColorTextureMatrix * attr_TexCoord0).st; + + // assign color + var_Color = u_Color; +} diff --git a/main/glsl/skybox_fp.glsl b/main/glsl/skybox_fp.glsl new file mode 100644 index 0000000000..e0141b5d21 --- /dev/null +++ b/main/glsl/skybox_fp.glsl @@ -0,0 +1,57 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* skybox_fp.glsl */ + +uniform samplerCube u_ColorMap; +uniform vec3 u_ViewOrigin; +uniform vec4 u_PortalPlane; + +varying vec3 var_Position; + +void main() +{ + #if defined(USE_PORTAL_CLIPPING) + { + float dist = dot(var_Position.xyz, u_PortalPlane.xyz) - u_PortalPlane.w; + if(dist < 0.0) + { + discard; + return; + } + } +#endif + + // compute incident ray + vec3 I = normalize(var_Position - u_ViewOrigin); + + vec4 color = textureCube(u_ColorMap, I).rgba; + +#if defined(r_DeferredShading) + gl_FragData[0] = color; + gl_FragData[1] = vec4(0.0); + gl_FragData[2] = vec4(0.0); + gl_FragData[3] = vec4(0.0); +#else + gl_FragColor = color; +#endif +} diff --git a/main/glsl/skybox_vp.glsl b/main/glsl/skybox_vp.glsl new file mode 100644 index 0000000000..577b6e6f50 --- /dev/null +++ b/main/glsl/skybox_vp.glsl @@ -0,0 +1,40 @@ +/* +=========================================================================== +Copyright (C) 2006-2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* skybox_vp.glsl */ + +attribute vec4 attr_Position; + +uniform mat4 u_ModelMatrix; +uniform mat4 u_ModelViewProjectionMatrix; + +varying vec3 var_Position; + +void main() +{ + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * attr_Position; + + // transform position into world space + var_Position = (u_ModelMatrix * attr_Position).xyz; +} + diff --git a/main/glsl/vertexAnimation_vp.glsl b/main/glsl/vertexAnimation_vp.glsl new file mode 100644 index 0000000000..b6bf4d5cab --- /dev/null +++ b/main/glsl/vertexAnimation_vp.glsl @@ -0,0 +1,56 @@ +/* +=========================================================================== +Copyright (C) 2010 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// vertexAnimation_vp.glsl - interpolates .md3/.mdc vertex animations + +vec4 InterpolatePosition(vec4 from, vec4 to, float frac) +{ + return from + ((to - from) * frac); +} + +vec3 InterpolateNormal(vec3 from, vec3 to, float frac) +{ + return normalize(from + ((to - from) * frac)); +} + +void VertexAnimation_P_N( vec4 fromPosition, vec4 toPosition, + vec3 fromNormal, vec3 toNormal, + float frac, + inout vec4 position, inout vec3 normal) +{ + position = InterpolatePosition(fromPosition, toPosition, frac); + + normal = InterpolateNormal(fromNormal, toNormal, frac); +} + +void VertexAnimation_P_TBN( vec4 fromPosition, vec4 toPosition, + vec3 fromTangent, vec3 toTangent, + vec3 fromBinormal, vec3 toBinormal, + vec3 fromNormal, vec3 toNormal, + float frac, + inout vec4 position, inout vec3 tangent, inout vec3 binormal, inout vec3 normal) +{ + position = InterpolatePosition(fromPosition, toPosition, frac); + + tangent = InterpolateNormal(fromTangent, toTangent, frac); + binormal = InterpolateNormal(fromBinormal, toBinormal, frac); + normal = InterpolateNormal(fromNormal, toNormal, frac); +} diff --git a/main/glsl/vertexLighting_DBS_entity_fp.glsl b/main/glsl/vertexLighting_DBS_entity_fp.glsl new file mode 100644 index 0000000000..764a98b75e --- /dev/null +++ b/main/glsl/vertexLighting_DBS_entity_fp.glsl @@ -0,0 +1,269 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* vertexLighting_DBS_entity_fp.glsl */ + +uniform sampler2D u_DiffuseMap; +uniform sampler2D u_NormalMap; +uniform sampler2D u_SpecularMap; + +uniform samplerCube u_EnvironmentMap0; +uniform samplerCube u_EnvironmentMap1; +uniform float u_EnvironmentInterpolation; + +uniform int u_AlphaTest; +uniform vec3 u_ViewOrigin; +uniform vec3 u_AmbientColor; +uniform vec3 u_LightDir; +uniform vec3 u_LightColor; +uniform float u_SpecularExponent; +uniform float u_DepthScale; +uniform vec4 u_PortalPlane; + +varying vec3 var_Position; +varying vec2 var_TexDiffuse; +#if defined(USE_NORMAL_MAPPING) +varying vec2 var_TexNormal; +varying vec2 var_TexSpecular; +varying vec3 var_Tangent; +varying vec3 var_Binormal; +#endif +varying vec3 var_Normal; + + + +void main() +{ +#if defined(USE_PORTAL_CLIPPING) + { + float dist = dot(var_Position.xyz, u_PortalPlane.xyz) - u_PortalPlane.w; + if(dist < 0.0) + { + discard; + return; + } + } +#endif + + // compute light direction in world space + vec3 L = u_LightDir; + + // compute view direction in world space + vec3 V = normalize(u_ViewOrigin - var_Position); + + vec2 texDiffuse = var_TexDiffuse.st; + +#if defined(USE_NORMAL_MAPPING) + // invert tangent space for two sided surfaces + mat3 tangentToWorldMatrix; + +#if defined(TWOSIDED) + if(gl_FrontFacing) + { + tangentToWorldMatrix = mat3(-var_Tangent.xyz, -var_Binormal.xyz, -var_Normal.xyz); + } + else +#endif + { + tangentToWorldMatrix = mat3(var_Tangent.xyz, var_Binormal.xyz, var_Normal.xyz); + } + + vec2 texNormal = var_TexNormal.st; + vec2 texSpecular = var_TexSpecular.st; + +#if defined(USE_PARALLAX_MAPPING) + + // ray intersect in view direction + + mat3 worldToTangentMatrix; + #if defined(GLHW_ATI) || defined(GLHW_ATI_DX10) || defined(GLDRV_MESA) + worldToTangentMatrix = mat3(tangentToWorldMatrix[0][0], tangentToWorldMatrix[1][0], tangentToWorldMatrix[2][0], + tangentToWorldMatrix[0][1], tangentToWorldMatrix[1][1], tangentToWorldMatrix[2][1], + tangentToWorldMatrix[0][2], tangentToWorldMatrix[1][2], tangentToWorldMatrix[2][2]); + #else + worldToTangentMatrix = transpose(tangentToWorldMatrix); + #endif + + // compute view direction in tangent space + vec3 Vts = worldToTangentMatrix * (u_ViewOrigin - var_Position.xyz); + Vts = normalize(Vts); + + // size and start position of search in texture space + vec2 S = Vts.xy * -u_DepthScale / Vts.z; + +#if 0 + vec2 texOffset = vec2(0.0); + for(int i = 0; i < 4; i++) { + vec4 Normal = texture2D(u_NormalMap, texNormal.st + texOffset); + float height = Normal.a * 0.2 - 0.0125; + texOffset += height * Normal.z * S; + } +#else + float depth = RayIntersectDisplaceMap(texNormal, S, u_NormalMap); + + // compute texcoords offset + vec2 texOffset = S * depth; +#endif + + texDiffuse.st += texOffset; + texNormal.st += texOffset; + texSpecular.st += texOffset; +#endif // USE_PARALLAX_MAPPING + + // compute normal in world space from normalmap + vec3 N = tangentToWorldMatrix * (2.0 * (texture2D(u_NormalMap, texNormal).xyz - 0.5)); + + // compute half angle in world space + vec3 H = normalize(L + V); + + // compute the specular term +#if defined(USE_REFLECTIVE_SPECULAR) + + vec3 specular = texture2D(u_SpecularMap, texSpecular).rgb; + + vec4 envColor0 = textureCube(u_EnvironmentMap0, reflect(-V, N)).rgba; + vec4 envColor1 = textureCube(u_EnvironmentMap1, reflect(-V, N)).rgba; + + specular *= mix(envColor0, envColor1, u_EnvironmentInterpolation).rgb; + + // Blinn-Phong + float NH = clamp(dot(N, H), 0, 1); + specular *= u_LightColor * pow(NH, r_SpecularExponent2) * r_SpecularScale; + +#if 0 + gl_FragColor = vec4(specular, 1.0); + // gl_FragColor = vec4(u_EnvironmentInterpolation, u_EnvironmentInterpolation, u_EnvironmentInterpolation, 1.0); + // gl_FragColor = envColor0; + return; +#endif + +#else + + // simple Blinn-Phong + float NH = clamp(dot(N, H), 0, 1); + vec3 specular = texture2D(u_SpecularMap, texSpecular).rgb * u_LightColor * pow(NH, r_SpecularExponent) * r_SpecularScale; + +#endif // USE_REFLECTIVE_SPECULAR + + +#else // USE_NORMAL_MAPPING + + vec3 N; + +#if defined(TWOSIDED) + if(gl_FrontFacing) + { + N = -normalize(var_Normal); + // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + // return; + } + else +#endif + { + N = normalize(var_Normal); + } + + vec3 specular = vec3(0.0); + +#endif // USE_NORMAL_MAPPING + + + // compute the diffuse term + vec4 diffuse = texture2D(u_DiffuseMap, texDiffuse); + +#if defined(USE_ALPHA_TESTING) + if(u_AlphaTest == ATEST_GT_0 && diffuse.a <= 0.0) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_LT_128 && diffuse.a >= 0.5) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_GE_128 && diffuse.a < 0.5) + { + discard; + return; + } +#endif + + +// add Rim Lighting to highlight the edges +#if defined(r_RimLighting) + float rim = 1.0 - clamp(dot(N, V), 0, 1); + vec3 emission = r_RimColor.rgb * pow(rim, r_RimExponent); + + // gl_FragColor = vec4(emission, 1.0); + // return; + +#endif + + // compute the light term +#if defined(r_HalfLambertLighting) + // http://developer.valvesoftware.com/wiki/Half_Lambert + float NL = dot(N, L) * 0.5 + 0.5; + NL *= NL; +#elif defined(r_WrapAroundLighting) + float NL = clamp(dot(N, L) + r_WrapAroundLighting, 0.0, 1.0) / clamp(1.0 + r_WrapAroundLighting, 0.0, 1.0); +#else + float NL = clamp(dot(N, L), 0.0, 1.0); +#endif + + vec3 light = u_AmbientColor + u_LightColor * NL; + clamp(light, 0.0, 1.0); + + // compute final color + vec4 color = diffuse; + color.rgb *= light; + color.rgb += specular; +#if defined(r_RimLighting) + color.rgb += emission; +#endif + + // convert normal to [0,1] color space + N = N * 0.5 + 0.5; + +#if defined(r_DeferredShading) + gl_FragData[0] = color; + gl_FragData[1] = vec4(diffuse.rgb, 0.0); + gl_FragData[2] = vec4(N, 0.0); + gl_FragData[3] = vec4(specular, 0.0); +#else + gl_FragColor = color; +#endif + + // gl_FragColor = vec4(vec3(NL, NL, NL), diffuse.a); + +#if 0 +#if defined(USE_PARALLAX_MAPPING) + gl_FragColor = vec4(vec3(1.0, 0.0, 0.0), diffuse.a); +#elif defined(USE_NORMAL_MAPPING) + gl_FragColor = vec4(vec3(0.0, 0.0, 1.0), diffuse.a); +#else + gl_FragColor = vec4(vec3(0.0, 1.0, 0.0), diffuse.a); +#endif +#endif +} + + diff --git a/main/glsl/vertexLighting_DBS_entity_vp.glsl b/main/glsl/vertexLighting_DBS_entity_vp.glsl new file mode 100644 index 0000000000..8b79b5b9d0 --- /dev/null +++ b/main/glsl/vertexLighting_DBS_entity_vp.glsl @@ -0,0 +1,133 @@ +/* +=========================================================================== +Copyright (C) 2006-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* vertexLighting_DBS_entity_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; +attribute vec3 attr_Tangent; +attribute vec3 attr_Binormal; +attribute vec3 attr_Normal; + +attribute vec4 attr_Position2; +attribute vec3 attr_Tangent2; +attribute vec3 attr_Binormal2; +attribute vec3 attr_Normal2; + +uniform float u_VertexInterpolation; + +uniform mat4 u_DiffuseTextureMatrix; +uniform mat4 u_NormalTextureMatrix; +uniform mat4 u_SpecularTextureMatrix; +uniform mat4 u_ModelMatrix; +uniform mat4 u_ModelViewProjectionMatrix; + +uniform float u_Time; + +varying vec3 var_Position; +varying vec2 var_TexDiffuse; +#if defined(USE_NORMAL_MAPPING) +varying vec2 var_TexNormal; +varying vec2 var_TexSpecular; +varying vec3 var_Tangent; +varying vec3 var_Binormal; +#endif +varying vec3 var_Normal; + + + + +void main() +{ + vec4 position; + vec3 tangent; + vec3 binormal; + vec3 normal; + +#if defined(USE_VERTEX_SKINNING) + + #if defined(USE_NORMAL_MAPPING) + VertexSkinning_P_TBN( attr_Position, attr_Tangent, attr_Binormal, attr_Normal, + position, tangent, binormal, normal); + #else + VertexSkinning_P_N( attr_Position, attr_Normal, + position, normal); + #endif + +#elif defined(USE_VERTEX_ANIMATION) + + #if defined(USE_NORMAL_MAPPING) + VertexAnimation_P_TBN( attr_Position, attr_Position2, + attr_Tangent, attr_Tangent2, + attr_Binormal, attr_Binormal2, + attr_Normal, attr_Normal2, + u_VertexInterpolation, + position, tangent, binormal, normal); + #else + VertexAnimation_P_N(attr_Position, attr_Position2, + attr_Normal, attr_Normal2, + u_VertexInterpolation, + position, normal); + #endif + +#else + position = attr_Position; + + #if defined(USE_NORMAL_MAPPING) + tangent = attr_Tangent; + binormal = attr_Binormal; + #endif + + normal = attr_Normal; +#endif + +#if defined(USE_DEFORM_VERTEXES) + position = DeformPosition2( position, + normal, + attr_TexCoord0.st, + u_Time); +#endif + + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * position; + + // transform position into world space + var_Position = (u_ModelMatrix * position).xyz; + + #if defined(USE_NORMAL_MAPPING) + var_Tangent.xyz = (u_ModelMatrix * vec4(tangent, 0.0)).xyz; + var_Binormal.xyz = (u_ModelMatrix * vec4(binormal, 0.0)).xyz; + #endif + + var_Normal.xyz = (u_ModelMatrix * vec4(normal, 0.0)).xyz; + + // transform diffusemap texcoords + var_TexDiffuse = (u_DiffuseTextureMatrix * attr_TexCoord0).st; + +#if defined(USE_NORMAL_MAPPING) + // transform normalmap texcoords + var_TexNormal = (u_NormalTextureMatrix * attr_TexCoord0).st; + + // transform specularmap texture coords + var_TexSpecular = (u_SpecularTextureMatrix * attr_TexCoord0).st; +#endif +} diff --git a/main/glsl/vertexLighting_DBS_world_fp.glsl b/main/glsl/vertexLighting_DBS_world_fp.glsl new file mode 100644 index 0000000000..6c734acc46 --- /dev/null +++ b/main/glsl/vertexLighting_DBS_world_fp.glsl @@ -0,0 +1,306 @@ +/* +=========================================================================== +Copyright (C) 2008-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* vertexLighting_DBS_world_fp.glsl */ + +uniform sampler2D u_DiffuseMap; +uniform sampler2D u_NormalMap; +uniform sampler2D u_SpecularMap; +uniform int u_AlphaTest; +uniform vec3 u_ViewOrigin; +uniform float u_DepthScale; +uniform int u_PortalClipping; +uniform vec4 u_PortalPlane; +uniform float u_LightWrapAround; + +varying vec3 var_Position; +varying vec4 var_TexDiffuseNormal; +varying vec2 var_TexSpecular; +varying vec4 var_LightColor; +#if !defined(COMPAT_Q3A) +varying vec3 var_LightDirection; +#endif +varying vec3 var_Tangent; +varying vec3 var_Binormal; +varying vec3 var_Normal; + + + +void main() +{ + if(bool(u_PortalClipping)) + { + float dist = dot(var_Position.xyz, u_PortalPlane.xyz) - u_PortalPlane.w; + if(dist < 0.0) + { + discard; + return; + } + } + +#if defined(USE_NORMAL_MAPPING) + + // construct object-space-to-tangent-space 3x3 matrix + mat3 objectToTangentMatrix; + +#if defined(TWOSIDED) + if(gl_FrontFacing) + { + objectToTangentMatrix = mat3( -var_Tangent.x, -var_Binormal.x, -var_Normal.x, + -var_Tangent.y, -var_Binormal.y, -var_Normal.y, + -var_Tangent.z, -var_Binormal.z, -var_Normal.z ); + } + else +#endif + { + objectToTangentMatrix = mat3( var_Tangent.x, var_Binormal.x, var_Normal.x, + var_Tangent.y, var_Binormal.y, var_Normal.y, + var_Tangent.z, var_Binormal.z, var_Normal.z ); + } + + // compute view direction in tangent space + vec3 V = normalize(objectToTangentMatrix * (u_ViewOrigin - var_Position)); + + vec2 texDiffuse = var_TexDiffuseNormal.st; + vec2 texNormal = var_TexDiffuseNormal.pq; + vec2 texSpecular = var_TexSpecular.st; + +#if defined(USE_PARALLAX_MAPPING) + + // ray intersect in view direction + + // size and start position of search in texture space + vec2 S = V.xy * -u_DepthScale / V.z; + +#if 0 + vec2 texOffset = vec2(0.0); + for(int i = 0; i < 4; i++) { + vec4 Normal = texture2D(u_NormalMap, texNormal.st + texOffset); + float height = Normal.a * 0.2 - 0.0125; + texOffset += height * Normal.z * S; + } +#else + float depth = RayIntersectDisplaceMap(texNormal, S, u_NormalMap); + + // compute texcoords offset + vec2 texOffset = S * depth; +#endif + + texDiffuse.st += texOffset; + texNormal.st += texOffset; + texSpecular.st += texOffset; +#endif // USE_PARALLAX_MAPPING + + // compute the diffuse term + vec4 diffuse = texture2D(u_DiffuseMap, texDiffuse); + +#if defined(USE_ALPHA_TESTING) + if(u_AlphaTest == ATEST_GT_0 && diffuse.a <= 0.0) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_LT_128 && diffuse.a >= 0.5) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_GE_128 && diffuse.a < 0.5) + { + discard; + return; + } +#endif + + // compute normal in tangent space from normalmap + vec3 N = 2.0 * (texture2D(u_NormalMap, texNormal).xyz - 0.5); + #if defined(r_NormalScale) + N.z *= r_NormalScale; + normalize(N); + #endif + +#if defined(COMPAT_Q3A) + // fake bump mapping + vec3 L = N; +#else + // compute light direction in tangent space + vec3 L = normalize(objectToTangentMatrix * var_LightDirection); +#endif + + // compute half angle in tangent space + vec3 H = normalize(L + V); + + // compute the light term +#if defined(r_WrapAroundLighting) + float NL = clamp(dot(N, L) + u_LightWrapAround, 0.0, 1.0) / clamp(1.0 + u_LightWrapAround, 0.0, 1.0); +#else + float NL = clamp(dot(N, L), 0.0, 1.0); +#endif + vec3 light = var_LightColor.rgb * NL; + + // compute the specular term + vec3 specular = texture2D(u_SpecularMap, texSpecular).rgb * var_LightColor.rgb * pow(clamp(dot(N, H), 0.0, 1.0), r_SpecularExponent) * r_SpecularScale; + + // compute final color + vec4 color = vec4(diffuse.rgb, var_LightColor.a); + color.rgb *= light; + color.rgb += specular; + +#if defined(r_DeferredShading) + gl_FragData[0] = color; // var_Color; + gl_FragData[1] = vec4(diffuse.rgb, var_LightColor.a); // vec4(var_Color.rgb, 1.0 - var_Color.a); + gl_FragData[2] = vec4(N, var_LightColor.a); + gl_FragData[3] = vec4(specular, var_LightColor.a); +#else + gl_FragColor = color; +#endif + + +#elif defined(COMPAT_Q3A) + + vec3 N; + +#if defined(TWOSIDED) + if(gl_FrontFacing) + { + N = -normalize(var_Normal); + } + else +#endif + { + N = normalize(var_Normal); + } + + // compute the diffuse term + vec4 diffuse = texture2D(u_DiffuseMap, var_TexDiffuseNormal.st); + +#if defined(USE_ALPHA_TESTING) + if(u_AlphaTest == ATEST_GT_0 && diffuse.a <= 0.0) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_LT_128 && diffuse.a >= 0.5) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_GE_128 && diffuse.a < 0.5) + { + discard; + return; + } +#endif + + vec4 color = vec4(diffuse.rgb * var_LightColor.rgb, var_LightColor.a); + + // gl_FragColor = vec4(diffuse.rgb * var_LightColor.rgb, diffuse.a); + // color = vec4(vec3(1.0, 0.0, 0.0), diffuse.a); + // gl_FragColor = vec4(vec3(diffuse.a, diffuse.a, diffuse.a), 1.0); + // gl_FragColor = vec4(vec3(var_LightColor.a, var_LightColor.a, var_LightColor.a), 1.0); + // gl_FragColor = var_LightColor; + +#if 0 //defined(r_ShowTerrainBlends) + color = vec4(vec3(var_LightColor.a), 1.0); +#endif + +#if defined(r_DeferredShading) + gl_FragData[0] = color; + gl_FragData[1] = vec4(diffuse.rgb, var_LightColor.a); + gl_FragData[2] = vec4(N, var_LightColor.a); + gl_FragData[3] = vec4(0.0, 0.0, 0.0, var_LightColor.a); +#else + gl_FragColor = color; +#endif + +#else // USE_NORMAL_MAPPING + + // compute the diffuse term + vec4 diffuse = texture2D(u_DiffuseMap, var_TexDiffuseNormal.st); + +#if defined(USE_ALPHA_TESTING) + if(u_AlphaTest == ATEST_GT_0 && diffuse.a <= 0.0) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_LT_128 && diffuse.a >= 0.5) + { + discard; + return; + } + else if(u_AlphaTest == ATEST_GE_128 && diffuse.a < 0.5) + { + discard; + return; + } +#endif + + vec3 N; + +#if defined(TWOSIDED) + if(gl_FrontFacing) + { + N = -normalize(var_Normal); + } + else +#endif + { + N = normalize(var_Normal); + } + + vec3 L = normalize(var_LightDirection); + + // compute the light term +#if defined(r_WrapAroundLighting) + float NL = clamp(dot(N, L) + u_LightWrapAround, 0.0, 1.0) / clamp(1.0 + u_LightWrapAround, 0.0, 1.0); +#else + float NL = clamp(dot(N, L), 0.0, 1.0); +#endif + + vec3 light = var_LightColor.rgb * NL; + + vec4 color = vec4(diffuse.rgb * light, var_LightColor.a); + // vec4 color = vec4(vec3(NL, NL, NL), diffuse.a); + +#if defined(r_DeferredShading) + gl_FragData[0] = color; // var_Color; + gl_FragData[1] = vec4(diffuse.rgb, var_LightColor.a); // vec4(var_Color.rgb, 1.0 - var_Color.a); + gl_FragData[2] = vec4(N, var_LightColor.a); + gl_FragData[3] = vec4(0.0, 0.0, 0.0, var_LightColor.a); +#else + gl_FragColor = color; +#endif + + +#endif // USE_NORMAL_MAPPING + +#if 0 +#if defined(USE_PARALLAX_MAPPING) + gl_FragColor = vec4(vec3(1.0, 0.0, 0.0), 1.0); +#elif defined(USE_NORMAL_MAPPING) + gl_FragColor = vec4(vec3(0.0, 0.0, 1.0), 1.0); +#else + gl_FragColor = vec4(vec3(0.0, 1.0, 0.0), 1.0); +#endif +#endif +} diff --git a/main/glsl/vertexLighting_DBS_world_vp.glsl b/main/glsl/vertexLighting_DBS_world_vp.glsl new file mode 100644 index 0000000000..88f3ad31a5 --- /dev/null +++ b/main/glsl/vertexLighting_DBS_world_vp.glsl @@ -0,0 +1,100 @@ +/* +=========================================================================== +Copyright (C) 2008-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* vertexLighting_DBS_world_vp.glsl */ + +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; +attribute vec3 attr_Tangent; +attribute vec3 attr_Binormal; +attribute vec3 attr_Normal; +attribute vec4 attr_Color; +#if !defined(COMPAT_Q3A) +attribute vec3 attr_LightDirection; +#endif + +uniform mat4 u_DiffuseTextureMatrix; +uniform mat4 u_NormalTextureMatrix; +uniform mat4 u_SpecularTextureMatrix; +uniform mat4 u_ModelViewProjectionMatrix; + +uniform float u_Time; + +uniform vec4 u_ColorModulate; +uniform vec4 u_Color; + +varying vec3 var_Position; +varying vec4 var_TexDiffuseNormal; +varying vec2 var_TexSpecular; +varying vec4 var_LightColor; +#if !defined(COMPAT_Q3A) +varying vec3 var_LightDirection; +#endif +varying vec3 var_Tangent; +varying vec3 var_Binormal; +varying vec3 var_Normal; + + + + +void main() +{ + vec4 position = attr_Position; +#if defined(USE_DEFORM_VERTEXES) + position = DeformPosition2( position, + attr_Normal, + attr_TexCoord0.st, + u_Time); +#endif + + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * position; + + // assign position in object space + var_Position = position.xyz; + + // transform diffusemap texcoords + var_TexDiffuseNormal.st = (u_DiffuseTextureMatrix * attr_TexCoord0).st; + +#if defined(USE_NORMAL_MAPPING) + // transform normalmap texcoords + var_TexDiffuseNormal.pq = (u_NormalTextureMatrix * attr_TexCoord0).st; + + // transform specularmap texture coords + var_TexSpecular = (u_SpecularTextureMatrix * attr_TexCoord0).st; +#endif + +#if !defined(COMPAT_Q3A) + // assign vertex to light vector in object space + var_LightDirection = attr_LightDirection; +#endif + + // assign color + var_LightColor = attr_Color * u_ColorModulate + u_Color; + +#if defined(USE_NORMAL_MAPPING) + var_Tangent = attr_Tangent; + var_Binormal = attr_Binormal; +#endif + + var_Normal = attr_Normal; +} diff --git a/main/glsl/vertexSkinning_vp.glsl b/main/glsl/vertexSkinning_vp.glsl new file mode 100644 index 0000000000..e437b26623 --- /dev/null +++ b/main/glsl/vertexSkinning_vp.glsl @@ -0,0 +1,115 @@ +/* +=========================================================================== +Copyright (C) 2009-2011 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// vertexSkinning_vp.glsl - GPU vertex skinning for skeletal meshes + +attribute vec4 attr_BoneIndexes; +attribute vec4 attr_BoneWeights; +uniform int u_VertexSkinning; +uniform mat4 u_BoneMatrix[MAX_GLSL_BONES]; + +void VertexSkinning_P_N(const vec4 inPosition, + const vec3 inNormal, + + inout vec4 position, + inout vec3 normal) +{ + position = vec4(0.0, 0.0, 0.0, 1.0); + normal = vec3(0.0); + +#if 1 + for(int i = 0; i < 4; i++) + { + int boneIndex = int(attr_BoneIndexes[i]); + float boneWeight = attr_BoneWeights[i]; + mat4 boneMatrix = u_BoneMatrix[boneIndex]; + + position.xyz += (boneMatrix * inPosition).xyz * boneWeight; + + normal += (mat3(boneMatrix) * inNormal) * boneWeight; + } +#else + // unrolled version + + // first bone + int boneIndex = int(attr_BoneIndexes[0]); + float boneWeight = attr_BoneWeights[0]; + mat4 boneMatrix = u_BoneMatrix[boneIndex]; + + position.xyz += (boneMatrix * inPosition).xyz * boneWeight; + normal += (boneMatrix * vec4(inNormal, 0.0)).xyz * boneWeight; + + // second bone + boneIndex = int(attr_BoneIndexes[1]); + boneWeight = attr_BoneWeights[1]; + boneMatrix = u_BoneMatrix[boneIndex]; + + position.xyz += (boneMatrix * inPosition).xyz * boneWeight; + normal += (boneMatrix * vec4(inNormal, 0.0)).xyz * boneWeight; + + // third + boneIndex = int(attr_BoneIndexes[2]); + boneWeight = attr_BoneWeights[2]; + boneMatrix = u_BoneMatrix[boneIndex]; + + position.xyz += (boneMatrix * inPosition).xyz * boneWeight; + normal += (boneMatrix * vec4(inNormal, 0.0)).xyz * boneWeight; + + // fourth + boneIndex = int(attr_BoneIndexes[3]); + boneWeight = attr_BoneWeights[3]; + boneMatrix = u_BoneMatrix[boneIndex]; + + position.xyz += (boneMatrix * inPosition).xyz * boneWeight; + normal += (boneMatrix * vec4(inNormal, 0.0)).xyz * boneWeight; + +#endif +} + +void VertexSkinning_P_TBN( const vec4 inPosition, + const vec3 inTangent, + const vec3 inBinormal, + const vec3 inNormal, + + inout vec4 position, + inout vec3 tangent, + inout vec3 binormal, + inout vec3 normal) +{ + position = vec4(0.0, 0.0, 0.0, 1.0); + + tangent = vec3(0.0); + binormal = vec3(0.0); + normal = vec3(0.0); + + for(int i = 0; i < 4; i++) + { + int boneIndex = int(attr_BoneIndexes[i]); + float boneWeight = attr_BoneWeights[i]; + mat4 boneMatrix = u_BoneMatrix[boneIndex]; + + position.xyz += (boneMatrix * inPosition).xyz * boneWeight; + + tangent += (boneMatrix * vec4(inTangent, 0.0)).xyz * boneWeight; + binormal += (boneMatrix * vec4(inBinormal, 0.0)).xyz * boneWeight; + normal += (boneMatrix * vec4(inNormal, 0.0)).xyz * boneWeight; + } +} diff --git a/main/glsl/volumetricFog_fp.glsl b/main/glsl/volumetricFog_fp.glsl new file mode 100644 index 0000000000..39cf80549b --- /dev/null +++ b/main/glsl/volumetricFog_fp.glsl @@ -0,0 +1,121 @@ +/* +=========================================================================== +Copyright (C) 2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* volumetricFog_fp.glsl */ + +uniform sampler2D u_DepthMap; // raw depth in red channel +uniform sampler2D u_DepthMapBack; // color encoded depth +uniform sampler2D u_DepthMapFront; // color encoded depth +uniform vec3 u_ViewOrigin; +uniform float u_FogDensity; +uniform vec3 u_FogColor; +uniform mat4 u_UnprojectMatrix; + + +float DecodeDepth(vec4 color) +{ + float depth; + + const vec4 bitShifts = vec4(1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0); // gl_FragCoord.z with 32 bit precision +// const vec4 bitShifts = vec4(1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0, 0.0); // gl_FragCoord.z with 24 bit precision + + depth = dot(color, bitShifts); + + return depth; +} + +void main() +{ + // calculate the screen texcoord in the 0.0 to 1.0 range + vec2 st = gl_FragCoord.st * r_FBufScale; + + // scale by the screen non-power-of-two-adjust + st *= r_NPOTScale; + + // calculate fog volume depth + float fogDepth; + + float depthSolid = texture2D(u_DepthMap, st).r; + +//#if 0 //defined(GLHW_ATI_DX10) || defined(GLHW_NV_DX10) +// +// float depthBack = texture2D(u_DepthMapBack, st).a; +// float depthFront = texture2D(u_DepthMapFront, st).a; +//#else + float depthBack = DecodeDepth(texture2D(u_DepthMapBack, st)); + float depthFront = DecodeDepth(texture2D(u_DepthMapFront, st)); +//#endif + + if(depthSolid < depthFront) + { + discard; + return; + } + + if(depthSolid < depthBack) + { + depthBack = depthSolid; + } + + fogDepth = depthSolid; + +//#if 1 + // reconstruct vertex position in world space + vec4 posBack = u_UnprojectMatrix * vec4(gl_FragCoord.xy, depthBack, 1.0); + posBack.xyz /= posBack.w; + + vec4 posFront = u_UnprojectMatrix * vec4(gl_FragCoord.xy, depthFront, 1.0); + posFront.xyz /= posFront.w; + + if(posFront.w <= 0.0) + { + // we might be in the volume + posFront = vec4(u_ViewOrigin, 1.0); + } + + // calculate fog distance + //if(depthFront < + float fogDistance = distance(posBack, posFront); + //float fogDistance = abs(depthBack - depthFront); + +/* +#elif 0 + vec4 P = u_UnprojectMatrix * vec4(gl_FragCoord.xy, depthBack - depthFront, 1.0); + P.xyz /= P.w; + + // calculate fog distance + float fogDistance = distance(P.xyz, u_ViewOrigin); + +#else + float fogDistance = depthBack - depthFront; +#endif +*/ + + // calculate fog exponent + float fogExponent = fogDistance * u_FogDensity; + + // calculate fog factor + float fogFactor = exp2(-abs(fogExponent)); + + // lerp between FBO color and fog color with GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA. GLS_DSTBLEND_SRC_ALPHA + gl_FragColor = vec4(u_FogColor, fogFactor); +} diff --git a/main/glsl/volumetricFog_vp.glsl b/main/glsl/volumetricFog_vp.glsl new file mode 100644 index 0000000000..f9677aab30 --- /dev/null +++ b/main/glsl/volumetricFog_vp.glsl @@ -0,0 +1,33 @@ +/* +=========================================================================== +Copyright (C) 2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL 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. + +XreaL 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 XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* volumetricFog_vp.glsl */ + +attribute vec4 attr_Position; + +uniform mat4 u_ModelViewProjectionMatrix; + +void main() +{ + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjectionMatrix * attr_Position; +} diff --git a/src/engine/OpenWolf.sln b/src/engine/OpenWolf.sln new file mode 100644 index 0000000000..0933d98efa --- /dev/null +++ b/src/engine/OpenWolf.sln @@ -0,0 +1,358 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "botlib", "botlib\botlib.vcxproj", "{EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenWolf", "OpenWolf.vcxproj", "{B604B554-38D6-4BC8-9F7F-D6BC8719A653}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glew", "..\libs\glew\build\vc6\glew_shared.vcxproj", "{583B8B2E-E56E-4A63-8697-0942C654AC5F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDL", "..\libs\libsdl\VisualC\SDL\SDL.vcxproj", "{81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDLmain", "..\libs\libsdl\VisualC\SDLmain\SDLmain.vcxproj", "{DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "renderer", "renderer\renderer.vcxproj", "{7906CC4F-E230-4049-A3EF-EA7F64E73DDE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rendererGL3", "rendererGL\rendererGL.vcxproj", "{A78CE356-910C-4C0E-886D-5E3F09F40082}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine", "Engine", "{82A5BFC3-148E-44BA-B3D1-0BF97DC11E19}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Renderer Engine", "Renderer Engine", "{4A545240-63A6-4A70-934A-93A50762BD4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ET Game Logic", "ET Game Logic", "{B21C759E-596A-4C10-B0AF-C0CA7430C9E1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Library Files", "Library Files", "{19015845-C78A-430B-BD2A-E864DC1C285A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OWMap", "..\tools\owmap\OWMap.vcxproj", "{A596DC61-F285-264E-AF49-24D6360F25B0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documents", "Documents", "{DA3FC87A-518B-46BC-8C5F-591FA52C6D5D}" + ProjectSection(SolutionItems) = preProject + ..\docs\BotScriptCommands = ..\docs\BotScriptCommands + ..\docs\Changelog = ..\docs\Changelog + ..\docs\Notes = ..\docs\Notes + ..\docs\QA-Changelog = ..\docs\QA-Changelog + ..\docs\Todo = ..\docs\Todo + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{79E077A7-DE41-4045-96DA-30D9AFDD92FB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tremulous Game Logic", "Tremulous Game Logic", "{52E7FCDB-F1FA-40F3-9EFA-3829D93E4106}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tremulous GPP Game Logic", "Tremulous GPP Game Logic", "{8ACC1AB8-EBBF-438C-AEE7-B5E211615FED}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Game Logic", "Game Logic", "{8167C31B-DFE2-46EB-AC47-AF725E19BBE9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OWMaster", "..\tools\owmaster\owmaster.vcxproj", "{49F16AE1-8677-4ECE-A882-DC6BBC73C44E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "snd_openal", "snd_openal\snd_openal.vcxproj", "{26EFB790-E3D6-436F-A7FC-36B9A00240D1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cgame", "..\gamelogic\etmain\src\cgame\cgame.vcxproj", "{68A804FC-00C3-44E2-A479-3DB3D20F943D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "game", "..\gamelogic\etmain\src\game\game.vcxproj", "{4F8332D7-5687-4875-813A-A1B7AA479C2D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ui", "..\gamelogic\etmain\src\ui\ui.vcxproj", "{4C02EEB1-33AA-41C5-9A96-609161DF1635}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cgame", "..\gamelogic\base\src\cgame\cgame.vcxproj", "{48033ADD-F9CE-4951-9520-2A86AC61E45B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "game", "..\gamelogic\base\src\game\game.vcxproj", "{6E90EE4B-CEA6-40F3-A5CC-8C96B7EA5F2E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ui", "..\gamelogic\base\src\ui\ui.vcxproj", "{75976B8A-6A6C-4CB9-B953-8633BD61181B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cgame", "..\gamelogic\gpp\src\cgame\cgame.vcxproj", "{C878E295-CB82-4B40-8ECF-5CE5525466FA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "game", "..\gamelogic\gpp\src\game\game.vcxproj", "{F9EE10DA-2404-4154-B904-F93C936C040A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ui", "..\gamelogic\gpp\src\ui\ui.vcxproj", "{D454C4C7-7765-4149-ABAD-05FDEB9D94F8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug Dedicated|Win32 = Debug Dedicated|Win32 + Debug Dedicated|x64 = Debug Dedicated|x64 + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release Dedicated|Win32 = Release Dedicated|Win32 + Release Dedicated|x64 = Release Dedicated|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Debug Dedicated|Win32.Build.0 = Debug|Win32 + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Debug Dedicated|x64.Build.0 = Debug|x64 + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Debug|Win32.ActiveCfg = Debug|Win32 + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Debug|Win32.Build.0 = Debug|Win32 + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Debug|x64.ActiveCfg = Debug|x64 + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Debug|x64.Build.0 = Debug|x64 + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Release Dedicated|Win32.Build.0 = Release|Win32 + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Release Dedicated|x64.ActiveCfg = Release|x64 + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Release Dedicated|x64.Build.0 = Release|x64 + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Release|Win32.ActiveCfg = Release|Win32 + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Release|Win32.Build.0 = Release|Win32 + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Release|x64.ActiveCfg = Release|x64 + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7}.Release|x64.Build.0 = Release|x64 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Debug Dedicated|Win32.ActiveCfg = Debug Dedicated|Win32 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Debug Dedicated|Win32.Build.0 = Debug Dedicated|Win32 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Debug Dedicated|x64.ActiveCfg = Debug Dedicated|x64 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Debug Dedicated|x64.Build.0 = Debug Dedicated|x64 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Debug|Win32.ActiveCfg = Debug|Win32 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Debug|Win32.Build.0 = Debug|Win32 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Debug|x64.ActiveCfg = Debug|x64 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Debug|x64.Build.0 = Debug|x64 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Release Dedicated|Win32.ActiveCfg = Release Dedicated|Win32 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Release Dedicated|Win32.Build.0 = Release Dedicated|Win32 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Release Dedicated|x64.ActiveCfg = Release Dedicated|x64 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Release Dedicated|x64.Build.0 = Release Dedicated|x64 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Release|Win32.ActiveCfg = Release|Win32 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Release|Win32.Build.0 = Release|Win32 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Release|x64.ActiveCfg = Release|x64 + {B604B554-38D6-4BC8-9F7F-D6BC8719A653}.Release|x64.Build.0 = Release|x64 + {583B8B2E-E56E-4A63-8697-0942C654AC5F}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {583B8B2E-E56E-4A63-8697-0942C654AC5F}.Debug Dedicated|Win32.Build.0 = Debug|Win32 + {583B8B2E-E56E-4A63-8697-0942C654AC5F}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {583B8B2E-E56E-4A63-8697-0942C654AC5F}.Debug|Win32.ActiveCfg = Debug|Win32 + {583B8B2E-E56E-4A63-8697-0942C654AC5F}.Debug|Win32.Build.0 = Debug|Win32 + {583B8B2E-E56E-4A63-8697-0942C654AC5F}.Debug|x64.ActiveCfg = Debug|x64 + {583B8B2E-E56E-4A63-8697-0942C654AC5F}.Debug|x64.Build.0 = Debug|x64 + {583B8B2E-E56E-4A63-8697-0942C654AC5F}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {583B8B2E-E56E-4A63-8697-0942C654AC5F}.Release Dedicated|x64.ActiveCfg = Release|x64 + {583B8B2E-E56E-4A63-8697-0942C654AC5F}.Release|Win32.ActiveCfg = Release|Win32 + {583B8B2E-E56E-4A63-8697-0942C654AC5F}.Release|Win32.Build.0 = Release|Win32 + {583B8B2E-E56E-4A63-8697-0942C654AC5F}.Release|x64.ActiveCfg = Release|x64 + {583B8B2E-E56E-4A63-8697-0942C654AC5F}.Release|x64.Build.0 = Release|x64 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug Dedicated|Win32.Build.0 = Debug|Win32 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug Dedicated|x64.Build.0 = Debug|x64 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug|Win32.ActiveCfg = Debug|Win32 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug|Win32.Build.0 = Debug|Win32 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug|x64.ActiveCfg = Debug|x64 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug|x64.Build.0 = Debug|x64 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release Dedicated|Win32.Build.0 = Release|Win32 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release Dedicated|x64.ActiveCfg = Release|x64 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release Dedicated|x64.Build.0 = Release|x64 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release|Win32.ActiveCfg = Release|Win32 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release|Win32.Build.0 = Release|Win32 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release|x64.ActiveCfg = Release|x64 + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release|x64.Build.0 = Release|x64 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Debug Dedicated|Win32.Build.0 = Debug|Win32 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Debug Dedicated|x64.Build.0 = Debug|x64 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Debug|Win32.ActiveCfg = Debug|Win32 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Debug|Win32.Build.0 = Debug|Win32 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Debug|x64.ActiveCfg = Debug|x64 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Debug|x64.Build.0 = Debug|x64 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release Dedicated|Win32.Build.0 = Release|Win32 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release Dedicated|x64.ActiveCfg = Release|x64 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release Dedicated|x64.Build.0 = Release|x64 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release|Win32.ActiveCfg = Release|Win32 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release|Win32.Build.0 = Release|Win32 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release|x64.ActiveCfg = Release|x64 + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release|x64.Build.0 = Release|x64 + {7906CC4F-E230-4049-A3EF-EA7F64E73DDE}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {7906CC4F-E230-4049-A3EF-EA7F64E73DDE}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {7906CC4F-E230-4049-A3EF-EA7F64E73DDE}.Debug|Win32.ActiveCfg = Debug|Win32 + {7906CC4F-E230-4049-A3EF-EA7F64E73DDE}.Debug|Win32.Build.0 = Debug|Win32 + {7906CC4F-E230-4049-A3EF-EA7F64E73DDE}.Debug|x64.ActiveCfg = Debug|x64 + {7906CC4F-E230-4049-A3EF-EA7F64E73DDE}.Debug|x64.Build.0 = Debug|x64 + {7906CC4F-E230-4049-A3EF-EA7F64E73DDE}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {7906CC4F-E230-4049-A3EF-EA7F64E73DDE}.Release Dedicated|x64.ActiveCfg = Release|x64 + {7906CC4F-E230-4049-A3EF-EA7F64E73DDE}.Release|Win32.ActiveCfg = Release|Win32 + {7906CC4F-E230-4049-A3EF-EA7F64E73DDE}.Release|Win32.Build.0 = Release|Win32 + {7906CC4F-E230-4049-A3EF-EA7F64E73DDE}.Release|x64.ActiveCfg = Release|x64 + {7906CC4F-E230-4049-A3EF-EA7F64E73DDE}.Release|x64.Build.0 = Release|x64 + {A78CE356-910C-4C0E-886D-5E3F09F40082}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {A78CE356-910C-4C0E-886D-5E3F09F40082}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {A78CE356-910C-4C0E-886D-5E3F09F40082}.Debug|Win32.ActiveCfg = Debug|Win32 + {A78CE356-910C-4C0E-886D-5E3F09F40082}.Debug|Win32.Build.0 = Debug|Win32 + {A78CE356-910C-4C0E-886D-5E3F09F40082}.Debug|x64.ActiveCfg = Debug|x64 + {A78CE356-910C-4C0E-886D-5E3F09F40082}.Debug|x64.Build.0 = Debug|x64 + {A78CE356-910C-4C0E-886D-5E3F09F40082}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {A78CE356-910C-4C0E-886D-5E3F09F40082}.Release Dedicated|x64.ActiveCfg = Release|x64 + {A78CE356-910C-4C0E-886D-5E3F09F40082}.Release|Win32.ActiveCfg = Release|Win32 + {A78CE356-910C-4C0E-886D-5E3F09F40082}.Release|Win32.Build.0 = Release|Win32 + {A78CE356-910C-4C0E-886D-5E3F09F40082}.Release|x64.ActiveCfg = Release|x64 + {A78CE356-910C-4C0E-886D-5E3F09F40082}.Release|x64.Build.0 = Release|x64 + {A596DC61-F285-264E-AF49-24D6360F25B0}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {A596DC61-F285-264E-AF49-24D6360F25B0}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {A596DC61-F285-264E-AF49-24D6360F25B0}.Debug|Win32.ActiveCfg = Debug|Win32 + {A596DC61-F285-264E-AF49-24D6360F25B0}.Debug|Win32.Build.0 = Debug|Win32 + {A596DC61-F285-264E-AF49-24D6360F25B0}.Debug|x64.ActiveCfg = Debug|x64 + {A596DC61-F285-264E-AF49-24D6360F25B0}.Debug|x64.Build.0 = Debug|x64 + {A596DC61-F285-264E-AF49-24D6360F25B0}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {A596DC61-F285-264E-AF49-24D6360F25B0}.Release Dedicated|x64.ActiveCfg = Release|x64 + {A596DC61-F285-264E-AF49-24D6360F25B0}.Release|Win32.ActiveCfg = Release|Win32 + {A596DC61-F285-264E-AF49-24D6360F25B0}.Release|Win32.Build.0 = Release|Win32 + {A596DC61-F285-264E-AF49-24D6360F25B0}.Release|x64.ActiveCfg = Release|x64 + {A596DC61-F285-264E-AF49-24D6360F25B0}.Release|x64.Build.0 = Release|x64 + {49F16AE1-8677-4ECE-A882-DC6BBC73C44E}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {49F16AE1-8677-4ECE-A882-DC6BBC73C44E}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {49F16AE1-8677-4ECE-A882-DC6BBC73C44E}.Debug|Win32.ActiveCfg = Debug|Win32 + {49F16AE1-8677-4ECE-A882-DC6BBC73C44E}.Debug|Win32.Build.0 = Debug|Win32 + {49F16AE1-8677-4ECE-A882-DC6BBC73C44E}.Debug|x64.ActiveCfg = Debug|x64 + {49F16AE1-8677-4ECE-A882-DC6BBC73C44E}.Debug|x64.Build.0 = Debug|x64 + {49F16AE1-8677-4ECE-A882-DC6BBC73C44E}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {49F16AE1-8677-4ECE-A882-DC6BBC73C44E}.Release Dedicated|x64.ActiveCfg = Release|x64 + {49F16AE1-8677-4ECE-A882-DC6BBC73C44E}.Release|Win32.ActiveCfg = Release|Win32 + {49F16AE1-8677-4ECE-A882-DC6BBC73C44E}.Release|Win32.Build.0 = Release|Win32 + {49F16AE1-8677-4ECE-A882-DC6BBC73C44E}.Release|x64.ActiveCfg = Release|x64 + {49F16AE1-8677-4ECE-A882-DC6BBC73C44E}.Release|x64.Build.0 = Release|x64 + {26EFB790-E3D6-436F-A7FC-36B9A00240D1}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {26EFB790-E3D6-436F-A7FC-36B9A00240D1}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {26EFB790-E3D6-436F-A7FC-36B9A00240D1}.Debug|Win32.ActiveCfg = Debug|Win32 + {26EFB790-E3D6-436F-A7FC-36B9A00240D1}.Debug|Win32.Build.0 = Debug|Win32 + {26EFB790-E3D6-436F-A7FC-36B9A00240D1}.Debug|x64.ActiveCfg = Debug|x64 + {26EFB790-E3D6-436F-A7FC-36B9A00240D1}.Debug|x64.Build.0 = Debug|x64 + {26EFB790-E3D6-436F-A7FC-36B9A00240D1}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {26EFB790-E3D6-436F-A7FC-36B9A00240D1}.Release Dedicated|x64.ActiveCfg = Release|x64 + {26EFB790-E3D6-436F-A7FC-36B9A00240D1}.Release|Win32.ActiveCfg = Release|Win32 + {26EFB790-E3D6-436F-A7FC-36B9A00240D1}.Release|Win32.Build.0 = Release|Win32 + {26EFB790-E3D6-436F-A7FC-36B9A00240D1}.Release|x64.ActiveCfg = Release|x64 + {26EFB790-E3D6-436F-A7FC-36B9A00240D1}.Release|x64.Build.0 = Release|x64 + {68A804FC-00C3-44E2-A479-3DB3D20F943D}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {68A804FC-00C3-44E2-A479-3DB3D20F943D}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {68A804FC-00C3-44E2-A479-3DB3D20F943D}.Debug Dedicated|x64.Build.0 = Debug|x64 + {68A804FC-00C3-44E2-A479-3DB3D20F943D}.Debug|Win32.ActiveCfg = Debug|Win32 + {68A804FC-00C3-44E2-A479-3DB3D20F943D}.Debug|Win32.Build.0 = Debug|Win32 + {68A804FC-00C3-44E2-A479-3DB3D20F943D}.Debug|x64.ActiveCfg = Debug|x64 + {68A804FC-00C3-44E2-A479-3DB3D20F943D}.Debug|x64.Build.0 = Debug|x64 + {68A804FC-00C3-44E2-A479-3DB3D20F943D}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {68A804FC-00C3-44E2-A479-3DB3D20F943D}.Release Dedicated|x64.ActiveCfg = Release|x64 + {68A804FC-00C3-44E2-A479-3DB3D20F943D}.Release|Win32.ActiveCfg = Release|Win32 + {68A804FC-00C3-44E2-A479-3DB3D20F943D}.Release|Win32.Build.0 = Release|Win32 + {68A804FC-00C3-44E2-A479-3DB3D20F943D}.Release|x64.ActiveCfg = Release|x64 + {68A804FC-00C3-44E2-A479-3DB3D20F943D}.Release|x64.Build.0 = Release|x64 + {4F8332D7-5687-4875-813A-A1B7AA479C2D}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {4F8332D7-5687-4875-813A-A1B7AA479C2D}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {4F8332D7-5687-4875-813A-A1B7AA479C2D}.Debug Dedicated|x64.Build.0 = Debug|x64 + {4F8332D7-5687-4875-813A-A1B7AA479C2D}.Debug|Win32.ActiveCfg = Debug|Win32 + {4F8332D7-5687-4875-813A-A1B7AA479C2D}.Debug|Win32.Build.0 = Debug|Win32 + {4F8332D7-5687-4875-813A-A1B7AA479C2D}.Debug|x64.ActiveCfg = Debug|x64 + {4F8332D7-5687-4875-813A-A1B7AA479C2D}.Debug|x64.Build.0 = Debug|x64 + {4F8332D7-5687-4875-813A-A1B7AA479C2D}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {4F8332D7-5687-4875-813A-A1B7AA479C2D}.Release Dedicated|x64.ActiveCfg = Release|x64 + {4F8332D7-5687-4875-813A-A1B7AA479C2D}.Release|Win32.ActiveCfg = Release|Win32 + {4F8332D7-5687-4875-813A-A1B7AA479C2D}.Release|Win32.Build.0 = Release|Win32 + {4F8332D7-5687-4875-813A-A1B7AA479C2D}.Release|x64.ActiveCfg = Release|x64 + {4F8332D7-5687-4875-813A-A1B7AA479C2D}.Release|x64.Build.0 = Release|x64 + {4C02EEB1-33AA-41C5-9A96-609161DF1635}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {4C02EEB1-33AA-41C5-9A96-609161DF1635}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {4C02EEB1-33AA-41C5-9A96-609161DF1635}.Debug|Win32.ActiveCfg = Debug|Win32 + {4C02EEB1-33AA-41C5-9A96-609161DF1635}.Debug|Win32.Build.0 = Debug|Win32 + {4C02EEB1-33AA-41C5-9A96-609161DF1635}.Debug|x64.ActiveCfg = Debug|x64 + {4C02EEB1-33AA-41C5-9A96-609161DF1635}.Debug|x64.Build.0 = Debug|x64 + {4C02EEB1-33AA-41C5-9A96-609161DF1635}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {4C02EEB1-33AA-41C5-9A96-609161DF1635}.Release Dedicated|x64.ActiveCfg = Release|x64 + {4C02EEB1-33AA-41C5-9A96-609161DF1635}.Release|Win32.ActiveCfg = Release|Win32 + {4C02EEB1-33AA-41C5-9A96-609161DF1635}.Release|Win32.Build.0 = Release|Win32 + {4C02EEB1-33AA-41C5-9A96-609161DF1635}.Release|x64.ActiveCfg = Release|x64 + {4C02EEB1-33AA-41C5-9A96-609161DF1635}.Release|x64.Build.0 = Release|x64 + {48033ADD-F9CE-4951-9520-2A86AC61E45B}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {48033ADD-F9CE-4951-9520-2A86AC61E45B}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {48033ADD-F9CE-4951-9520-2A86AC61E45B}.Debug Dedicated|x64.Build.0 = Debug|x64 + {48033ADD-F9CE-4951-9520-2A86AC61E45B}.Debug|Win32.ActiveCfg = Debug|Win32 + {48033ADD-F9CE-4951-9520-2A86AC61E45B}.Debug|Win32.Build.0 = Debug|Win32 + {48033ADD-F9CE-4951-9520-2A86AC61E45B}.Debug|x64.ActiveCfg = Debug|x64 + {48033ADD-F9CE-4951-9520-2A86AC61E45B}.Debug|x64.Build.0 = Debug|x64 + {48033ADD-F9CE-4951-9520-2A86AC61E45B}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {48033ADD-F9CE-4951-9520-2A86AC61E45B}.Release Dedicated|x64.ActiveCfg = Release|x64 + {48033ADD-F9CE-4951-9520-2A86AC61E45B}.Release|Win32.ActiveCfg = Release|Win32 + {48033ADD-F9CE-4951-9520-2A86AC61E45B}.Release|Win32.Build.0 = Release|Win32 + {48033ADD-F9CE-4951-9520-2A86AC61E45B}.Release|x64.ActiveCfg = Release|x64 + {48033ADD-F9CE-4951-9520-2A86AC61E45B}.Release|x64.Build.0 = Release|x64 + {6E90EE4B-CEA6-40F3-A5CC-8C96B7EA5F2E}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {6E90EE4B-CEA6-40F3-A5CC-8C96B7EA5F2E}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {6E90EE4B-CEA6-40F3-A5CC-8C96B7EA5F2E}.Debug Dedicated|x64.Build.0 = Debug|x64 + {6E90EE4B-CEA6-40F3-A5CC-8C96B7EA5F2E}.Debug|Win32.ActiveCfg = Debug|Win32 + {6E90EE4B-CEA6-40F3-A5CC-8C96B7EA5F2E}.Debug|Win32.Build.0 = Debug|Win32 + {6E90EE4B-CEA6-40F3-A5CC-8C96B7EA5F2E}.Debug|x64.ActiveCfg = Debug|x64 + {6E90EE4B-CEA6-40F3-A5CC-8C96B7EA5F2E}.Debug|x64.Build.0 = Debug|x64 + {6E90EE4B-CEA6-40F3-A5CC-8C96B7EA5F2E}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {6E90EE4B-CEA6-40F3-A5CC-8C96B7EA5F2E}.Release Dedicated|x64.ActiveCfg = Release|x64 + {6E90EE4B-CEA6-40F3-A5CC-8C96B7EA5F2E}.Release|Win32.ActiveCfg = Release|Win32 + {6E90EE4B-CEA6-40F3-A5CC-8C96B7EA5F2E}.Release|Win32.Build.0 = Release|Win32 + {6E90EE4B-CEA6-40F3-A5CC-8C96B7EA5F2E}.Release|x64.ActiveCfg = Release|x64 + {6E90EE4B-CEA6-40F3-A5CC-8C96B7EA5F2E}.Release|x64.Build.0 = Release|x64 + {75976B8A-6A6C-4CB9-B953-8633BD61181B}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {75976B8A-6A6C-4CB9-B953-8633BD61181B}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {75976B8A-6A6C-4CB9-B953-8633BD61181B}.Debug|Win32.ActiveCfg = Debug|Win32 + {75976B8A-6A6C-4CB9-B953-8633BD61181B}.Debug|Win32.Build.0 = Debug|Win32 + {75976B8A-6A6C-4CB9-B953-8633BD61181B}.Debug|x64.ActiveCfg = Debug|x64 + {75976B8A-6A6C-4CB9-B953-8633BD61181B}.Debug|x64.Build.0 = Debug|x64 + {75976B8A-6A6C-4CB9-B953-8633BD61181B}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {75976B8A-6A6C-4CB9-B953-8633BD61181B}.Release Dedicated|x64.ActiveCfg = Release|x64 + {75976B8A-6A6C-4CB9-B953-8633BD61181B}.Release|Win32.ActiveCfg = Release|Win32 + {75976B8A-6A6C-4CB9-B953-8633BD61181B}.Release|Win32.Build.0 = Release|Win32 + {75976B8A-6A6C-4CB9-B953-8633BD61181B}.Release|x64.ActiveCfg = Release|x64 + {75976B8A-6A6C-4CB9-B953-8633BD61181B}.Release|x64.Build.0 = Release|x64 + {C878E295-CB82-4B40-8ECF-5CE5525466FA}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {C878E295-CB82-4B40-8ECF-5CE5525466FA}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {C878E295-CB82-4B40-8ECF-5CE5525466FA}.Debug Dedicated|x64.Build.0 = Debug|x64 + {C878E295-CB82-4B40-8ECF-5CE5525466FA}.Debug|Win32.ActiveCfg = Debug|Win32 + {C878E295-CB82-4B40-8ECF-5CE5525466FA}.Debug|Win32.Build.0 = Debug|Win32 + {C878E295-CB82-4B40-8ECF-5CE5525466FA}.Debug|x64.ActiveCfg = Debug|x64 + {C878E295-CB82-4B40-8ECF-5CE5525466FA}.Debug|x64.Build.0 = Debug|x64 + {C878E295-CB82-4B40-8ECF-5CE5525466FA}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {C878E295-CB82-4B40-8ECF-5CE5525466FA}.Release Dedicated|x64.ActiveCfg = Release|x64 + {C878E295-CB82-4B40-8ECF-5CE5525466FA}.Release|Win32.ActiveCfg = Release|Win32 + {C878E295-CB82-4B40-8ECF-5CE5525466FA}.Release|Win32.Build.0 = Release|Win32 + {C878E295-CB82-4B40-8ECF-5CE5525466FA}.Release|x64.ActiveCfg = Release|x64 + {C878E295-CB82-4B40-8ECF-5CE5525466FA}.Release|x64.Build.0 = Release|x64 + {F9EE10DA-2404-4154-B904-F93C936C040A}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {F9EE10DA-2404-4154-B904-F93C936C040A}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {F9EE10DA-2404-4154-B904-F93C936C040A}.Debug Dedicated|x64.Build.0 = Debug|x64 + {F9EE10DA-2404-4154-B904-F93C936C040A}.Debug|Win32.ActiveCfg = Debug|Win32 + {F9EE10DA-2404-4154-B904-F93C936C040A}.Debug|Win32.Build.0 = Debug|Win32 + {F9EE10DA-2404-4154-B904-F93C936C040A}.Debug|x64.ActiveCfg = Debug|x64 + {F9EE10DA-2404-4154-B904-F93C936C040A}.Debug|x64.Build.0 = Debug|x64 + {F9EE10DA-2404-4154-B904-F93C936C040A}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {F9EE10DA-2404-4154-B904-F93C936C040A}.Release Dedicated|x64.ActiveCfg = Release|x64 + {F9EE10DA-2404-4154-B904-F93C936C040A}.Release|Win32.ActiveCfg = Release|Win32 + {F9EE10DA-2404-4154-B904-F93C936C040A}.Release|Win32.Build.0 = Release|Win32 + {F9EE10DA-2404-4154-B904-F93C936C040A}.Release|x64.ActiveCfg = Release|x64 + {F9EE10DA-2404-4154-B904-F93C936C040A}.Release|x64.Build.0 = Release|x64 + {D454C4C7-7765-4149-ABAD-05FDEB9D94F8}.Debug Dedicated|Win32.ActiveCfg = Debug|Win32 + {D454C4C7-7765-4149-ABAD-05FDEB9D94F8}.Debug Dedicated|x64.ActiveCfg = Debug|x64 + {D454C4C7-7765-4149-ABAD-05FDEB9D94F8}.Debug|Win32.ActiveCfg = Debug|Win32 + {D454C4C7-7765-4149-ABAD-05FDEB9D94F8}.Debug|Win32.Build.0 = Debug|Win32 + {D454C4C7-7765-4149-ABAD-05FDEB9D94F8}.Debug|x64.ActiveCfg = Debug|x64 + {D454C4C7-7765-4149-ABAD-05FDEB9D94F8}.Debug|x64.Build.0 = Debug|x64 + {D454C4C7-7765-4149-ABAD-05FDEB9D94F8}.Release Dedicated|Win32.ActiveCfg = Release|Win32 + {D454C4C7-7765-4149-ABAD-05FDEB9D94F8}.Release Dedicated|x64.ActiveCfg = Release|x64 + {D454C4C7-7765-4149-ABAD-05FDEB9D94F8}.Release|Win32.ActiveCfg = Release|Win32 + {D454C4C7-7765-4149-ABAD-05FDEB9D94F8}.Release|Win32.Build.0 = Release|Win32 + {D454C4C7-7765-4149-ABAD-05FDEB9D94F8}.Release|x64.ActiveCfg = Release|x64 + {D454C4C7-7765-4149-ABAD-05FDEB9D94F8}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {B604B554-38D6-4BC8-9F7F-D6BC8719A653} = {82A5BFC3-148E-44BA-B3D1-0BF97DC11E19} + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7} = {82A5BFC3-148E-44BA-B3D1-0BF97DC11E19} + {26EFB790-E3D6-436F-A7FC-36B9A00240D1} = {82A5BFC3-148E-44BA-B3D1-0BF97DC11E19} + {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68} = {19015845-C78A-430B-BD2A-E864DC1C285A} + {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A} = {19015845-C78A-430B-BD2A-E864DC1C285A} + {583B8B2E-E56E-4A63-8697-0942C654AC5F} = {19015845-C78A-430B-BD2A-E864DC1C285A} + {A78CE356-910C-4C0E-886D-5E3F09F40082} = {4A545240-63A6-4A70-934A-93A50762BD4B} + {7906CC4F-E230-4049-A3EF-EA7F64E73DDE} = {4A545240-63A6-4A70-934A-93A50762BD4B} + {52E7FCDB-F1FA-40F3-9EFA-3829D93E4106} = {8167C31B-DFE2-46EB-AC47-AF725E19BBE9} + {8ACC1AB8-EBBF-438C-AEE7-B5E211615FED} = {8167C31B-DFE2-46EB-AC47-AF725E19BBE9} + {B21C759E-596A-4C10-B0AF-C0CA7430C9E1} = {8167C31B-DFE2-46EB-AC47-AF725E19BBE9} + {A596DC61-F285-264E-AF49-24D6360F25B0} = {79E077A7-DE41-4045-96DA-30D9AFDD92FB} + {49F16AE1-8677-4ECE-A882-DC6BBC73C44E} = {79E077A7-DE41-4045-96DA-30D9AFDD92FB} + {48033ADD-F9CE-4951-9520-2A86AC61E45B} = {52E7FCDB-F1FA-40F3-9EFA-3829D93E4106} + {6E90EE4B-CEA6-40F3-A5CC-8C96B7EA5F2E} = {52E7FCDB-F1FA-40F3-9EFA-3829D93E4106} + {75976B8A-6A6C-4CB9-B953-8633BD61181B} = {52E7FCDB-F1FA-40F3-9EFA-3829D93E4106} + {C878E295-CB82-4B40-8ECF-5CE5525466FA} = {8ACC1AB8-EBBF-438C-AEE7-B5E211615FED} + {F9EE10DA-2404-4154-B904-F93C936C040A} = {8ACC1AB8-EBBF-438C-AEE7-B5E211615FED} + {D454C4C7-7765-4149-ABAD-05FDEB9D94F8} = {8ACC1AB8-EBBF-438C-AEE7-B5E211615FED} + {68A804FC-00C3-44E2-A479-3DB3D20F943D} = {B21C759E-596A-4C10-B0AF-C0CA7430C9E1} + {4F8332D7-5687-4875-813A-A1B7AA479C2D} = {B21C759E-596A-4C10-B0AF-C0CA7430C9E1} + {4C02EEB1-33AA-41C5-9A96-609161DF1635} = {B21C759E-596A-4C10-B0AF-C0CA7430C9E1} + EndGlobalSection +EndGlobal diff --git a/src/engine/OpenWolf.vcxproj b/src/engine/OpenWolf.vcxproj new file mode 100644 index 0000000000..be6d09c998 --- /dev/null +++ b/src/engine/OpenWolf.vcxproj @@ -0,0 +1,1231 @@ + + + + + Debug Dedicated + Win32 + + + Debug Dedicated + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release Dedicated + Win32 + + + Release Dedicated + x64 + + + Release + Win32 + + + Release + x64 + + + + {B604B554-38D6-4BC8-9F7F-D6BC8719A653} + wolf + + + + Application + false + + + Application + false + + + Application + false + + + Application + false + + + Application + false + + + Application + false + + + Application + false + + + Application + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + .\Debug\x32\ + .\Debug\x64 + .\Debug\x32\ + .\Debug\x64 + false + true + .\Debug_Dedicated\x32\ + .\Debug_Dedicated\x64\ + .\Debug_Dedicated\x32\ + .\Debug_Dedicated\x64\ + true + true + .\Release\x32\ + .\Release\x64\ + .\Release\x32\ + .\Release\x64\ + false + false + .\Release_Dedicated\x32\ + .\Release_Dedicated\x64\ + .\Release_Dedicated\x32\ + .\Release_Dedicated\x64\ + false + false + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + $(IncludePath) + $(IncludePath) + $(LibraryPath) + $(LibraryPath) + + + + + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/wolf.tlb + + + Disabled + MultiThreadedDebugDLL + + + .\Debug\x32\openwolf_x32.pch + .\Debug\x32\ + .\Debug\x32\ + .\Debug\x32\ + true + Level4 + true + EditAndContinue + StreamingSIMDExtensions2 + WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;USE_OPENSSL;USE_CODEC_VORBIS;USE_OPENAL;USE_OPENAL_DLOPEN;USE_CIN_THEORA;USE_INCREASED_ENTITIES;ZLIB_WINAPI;HAVE_BZIP2;USE_BASS;USE_VOIP;USE_MUMBLE;USE_PHYSICS;USE_CRYPTO;HAVE_CONFIG_H;%(PreprocessorDefinitions) + 16Bytes + true + true + Async + ..\libs\libtheora\include;..\libs\libvorbis\include;..\libs\openal\include;..\libs\mysql\include;..\libs\cpuinfo;..\libs\libspeex\include;..\libs\xvidcore\include;..\libs\zlibwapi\include;..\libs\libsdl\include;..\libs\libpng;..\libs\glew\include;..\libs\ruby\lib\ruby\1.8\i386-mswin32;..\libs\openssl\include;..\libs\curl-7.21.6\include;..\libs\libogg\include;..\libs\llvm\include;..\libs\bass\include;..\libs\libbz2\include;..\libs\libnewton\include;..\libs\libnewton\include\dContainers;..\libs\libnewton\include\dMath;..\libs\msinttypes;..\libs\gmp\include;..\libs\nettle\;%(AdditionalIncludeDirectories) + false + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + sys\winquake.res + + + + + + + + + dinput8.lib;winmm.lib;wsock32.lib;Iphlpapi.lib;libcurld_imp.lib;ws2_32.lib;glu32.lib;libmysql.lib;xvidcore.lib;psapi.lib;opengl32.lib;SDLd.lib;SDLMaind.lib;glew32d.lib;CPUInfod.lib;advapi32.lib;GlU32.Lib;libeay32.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;libtheora.lib;zlibwapi.lib;OpenAL32.lib;libbz2.lib;bass.lib;newton.lib;libgmp.a;%(AdditionalDependencies) + ../../bin/win32/debug/OpenWolf.exe + true + ../../bin/win32/debug/OpenWolf.intermediate.manifest + + + true + ../../bin/win32/debug/OpenWolf.pdb + true + ../../bin/win32/debug/OpenWolf.map + Console + 8388608 + false + + + + + ..\libs\libvorbis\libs\win32\Debug;..\libs\curl-7.21.6\lib\win32\debug;..\libs\mysql\lib\win32;..\libs\xvidcore\lib;..\libs\libsdl\libs\x32;..\libs\zlibwapi\lib\x32;..\libs\glew\lib\x32;..\libs\cpuinfo\lib\x32\debug;..\libs\ruby\libs\win32;..\libs\openssl\libs\win32;..\libs\libogg\libs\win32\debug;..\libs\libspeex\libs\win32\debug;..\libs\libtheora\libs\win32\debug;..\libs\Ruby\lib;..\libs\openal\libs\win32;.\botlib\Debug\x32;..\libs\libbz2\libs\x32;..\libs\bass\libs\win32;..\libs\llvm\libs\win32\debug\;..\libs\libnewton\libs\win32;..\libs\gmp\libs\win32;%(AdditionalLibraryDirectories) + + + + + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + .\Debug/wolf.tlb + + + Disabled + WIN32;_AMD64_;_WIN64;__WIN64__;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;USE_CODEC_VORBIS;USE_VOIP;USE_IRC;USE_OPENAL;USE_OPENAL_DLOPEN;USE_CIN_THEORA;USE_MUMBLE;USE_BASS;USE_CURL;USE_CRYPTO;USE_PHYSICS;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + + + .\Debug\x64\openwolf_x64.pch + .\Debug/ + .\Debug/ + .\Debug/ + true + Level3 + true + ProgramDatabase + ..\libs\mysql\include;..\libs\cpuinfo;..\libs\zlibwapi\include;..\libs\libsdl\include;..\libs\libpng;..\libs\glew\include\;..\libs\curl-7.21.6\include;..\libs\libvorbis\include;..\libs\libogg\include;..\libs\libspeex\win32;..\libs\libspeex\libspeex;..\libs\libspeex\include;..\libs\openal\include;..\libs\libtheora\include;..\libs\bass\include;..\libs\libbz2\include;..\libs\llvm\include;..\libs\libnewton\include\;..\libs\libnewton\include\dMath;..\libs\libnewton\include\dContainers;..\libs\msinttypes;..\libs\openssl\include;..\libs\nettle;..\libs\gmp\include;%(AdditionalIncludeDirectories) + StreamingSIMDExtensions2 + true + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + sys\winquake.res + + + + + + + + + /MACHINE:X64 %(AdditionalOptions) + user32.lib;shell32.lib;libcurld_imp.lib;dinput8.lib;winmm.lib;wsock32.lib;Iphlpapi.lib;ws2_32.lib;glu32.lib;libmysql.lib;psapi.lib;opengl32.lib;SDLd.lib;SDLmaind.lib;glew64d.lib;CPUInfod.lib;advapi32.lib;GlU32.Lib;zlibwapi.lib;libvorbis.lib;libvorbisfile.lib;libogg.lib;libtheora.lib;newton.lib;bass.lib;libbz2.lib;libeay32.lib;libgmp.a;%(AdditionalDependencies) + ../../bin/win64/debug/OpenWolf.exe + true + ..\libs\curl-7.21.6\lib\win64\debug;..\libs\mysql\lib\win64;..\libs\libsdl\libs\x64;..\libs\glew\lib\x64;..\libs\cpuinfo\lib\x64\debug;..\libs\openssl\lib\VC;..\libs\zlibwapi\lib\x64;..\libs\libvorbis\libs\x64\Debug;..\libs\libogg\libs\win64\debug;..\libs\libtheora\libs\win64\debug;..\libs\bass\libs\win64;..\libs\libbz2\libs\x64;..\libs\llvm\libs\win64\debug;..\libs\libnewton\libs\win64;..\libs\openssl\libs\win64;..\libs\gmp\libs\win64;%(AdditionalLibraryDirectories) + ../../bin/win64/debug/OpenWolf.intermediate.manifest + libcmtd.lib;%(IgnoreSpecificDefaultLibraries) + true + ../../bin/win64/debug/OpenWolf.pdb + true + ../../bin/win64/debug/OpenWolf.map + Console + 8388608 + false + + + + + + + + + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug_Dedicated/wolf.tlb + + + Disabled + WIN32;_DEBUG;_WINDOWS;DEDICATED;_CRT_SECURE_NO_WARNINGS;USE_OPENSSL;USE_CODEC_VORBIS;USE_VOIP;USE_OPENAL;USE_OPENAL_DLOPEN;USE_CIN_THEORA;USE_MUMBLE;USE_INCREASED_ENTITIES;ZLIB_WINAPI;USE_PHYSICS;USE_CRYPTO;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + + + .\Debug_Dedicated\x32\OpenWolf_x32.pch + .\Debug_Dedicated\x32\ + .\Debug_Dedicated\x32\ + .\Debug_Dedicated\x32\ + true + Level3 + true + EditAndContinue + ..\libs\libtheora\include;..\libs\libvorbis\include;..\libs\openal\include;..\libs\mysql\include;..\libs\cpuinfo;..\libs\speex\include;..\libs\xvidcore\include;..\libs\zlibwapi\include;..\libs\libsdl\include;..\libs\libpng;..\libs\glew\include\;..\libs\openssl\include;..\libs\curl-7.21.6\include;..\libs\libogg\include;..\libs\libspeex\include;..\libs\libspeex\libspeex;..\libs\libnewton\include;..\libs\libnewton\include\dContainers;..\libs\libnewton\include\dMath;..\libs\msinttypes;..\libs\gmp\include;..\libs\nettle;%(AdditionalIncludeDirectories) + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + sys\winquake.res + + + /MACHINE:I386 %(AdditionalOptions) + dinput8.lib;winmm.lib;wsock32.lib;Iphlpapi.lib;libcurld_imp.lib;ws2_32.lib;glu32.lib;libmysql.lib;xvidcore.lib;psapi.lib;opengl32.lib;SDLd.lib;SDLMaind.lib;newton.lib;glew32d.lib;CPUInfod.lib;advapi32.lib;GlU32.Lib;libeay32.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;libspeex.lib;libspeexdsp.lib;libtheora.lib;zlibwapi.lib;OpenAL32.lib;libgmp.a;%(AdditionalDependencies) + ../../bin/win32/debug/OpenWolf-dedicated.exe + true + ../../bin32/debug/OpenWolf_x32-dedicated.intermediate.manifest + true + ../../bin/win32/debug/OpenWolf-dedicated.pdb + true + ../../bin/win32/debug/OpenWolf-dedicated.map + Console + 8388608 + false + + + ..\libs\libvorbis\libs\win32\Debug;..\libs\curl-7.21.6\lib\win32\debug;..\libs\mysql\lib\win32;..\libs\xvidcore\lib;..\libs\libsdl\libs\x32;..\libs\zlibwapi\lib\x32;..\libs\glew\lib\x32;..\libs\cpuinfo\lib\x32\debug;..\libs\ruby\lib;..\libs\openssl\libs\win32;..\libs\libogg\libs\win32\debug;..\libs\libvorbis\libs\Win32\Debug;..\libs\libspeex\libs\win32\debug;..\libs\libtheora\libs\win32\debug;..\libs\Ruby\lib;..\libs\openal\libs\win32;..\libs\libnewton\libs\win32\static;..\libs\gmp\libs\win32;%(AdditionalLibraryDirectories) + libcmtd.lib;%(IgnoreSpecificDefaultLibraries) + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + .\Debug_Dedicated/wolf.tlb + + + Disabled + WIN32;_AMD64_;_WIN64;__WIN64__;_DEBUG;_WINDOWS;DEDICATED;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;USE_MUMBLE;USE_PHYSICS;USE_CRYPTO;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + + + .\Debug_Dedicated\x64\OpenWolf_x64.pch + .\Debug_Dedicated\x64\ + .\Debug_Dedicated\x64\ + .\Debug_Dedicated\x64\ + true + Level3 + true + ProgramDatabase + ..\libs\libtheora\include;..\libs\libvorbis\include;..\libs\openal\include;..\libs\mysql\include;..\libs\cpuinfo;..\libs\libspeex\include;..\libs\zlibwapi\include;..\libs\libsdl\include;..\libs\libpng;..\libs\glew\include;..\libs\openssl\include;..\libs\curl-7.21.6\include;..\libs\libogg\include;..\libs\libspeex\libspeex;..\libs\bass\include;..\libs\libbz2\include;..\libs\msinttypes;..\libs\libnewton\include;..\libs\libnewton\include\dContainers;..\libs\libnewton\include\dMath;..\libs\nettle;..\libs\gmp\include;%(AdditionalIncludeDirectories) + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + win32\winquake.res + + + user32.lib;shell32.lib;libcurld_imp.lib;dinput8.lib;winmm.lib;wsock32.lib;Iphlpapi.lib;ws2_32.lib;glu32.lib;libmysql.lib;psapi.lib;opengl32.lib;SDLd.lib;glew64d.lib;CPUInfod.lib;advapi32.lib;GlU32.Lib;zlibwapi.lib;libvorbis.lib;libvorbisfile.lib;libspeex.lib;libspeexdsp.lib;libogg.lib;libtheora.lib;newton.lib;OpenAL32.lib;botlib.lib;SDLmaind.lib;libgmp.a;%(AdditionalDependencies) + ../../bin/win64/debug/OpenWolf-dedicated.exe + true + ../../bin64/debug/OpenWolf_x64-dedicated.intermediate.manifest + true + ../../bin/win64/debug/OpenWolf-dedicated.pdb + true + ../../bin/win64/debug/OpenWolf-dedicated.map + Console + 8388608 + false + + + libcmtd.lib;%(IgnoreSpecificDefaultLibraries) + ..\libs\curl-7.21.6\lib\win64\debug;..\libs\mysql\lib\win64;..\libs\libsdl\libs\x64;..\libs\glew\lib\x64;..\libs\cpuinfo\lib\x64\debug;..\libs\openssl\lib\VC;..\libs\zlibwapi\lib\x64;..\libs\libvorbis\libs\x64\Debug;..\libs\libspeex\libs\win64\debug;..\libs\libogg\libs\win64\debug;..\libs\libtheora\libs\win64\debug;..\libs\libnewton\libs\win64\static;..\libs\openal\libs\win64;.\botlib\Debug\x64;..\libs\gmp\libs\win64;%(AdditionalLibraryDirectories) + + + + + + + + + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/wolf.tlb + + + OnlyExplicitInline + WIN32;_WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;USE_OPENSSL;USE_CODEC_VORBIS;USE_VOIP;USE_OPENAL;USE_OPENAL_DLOPEN;USE_CIN_THEORA;USE_MUMBLE;USE_INCREASED_ENTITIES;ZLIB_WINAPI;USE_BASS;USE_REFENTITY_ANIMATIONSYSTEM;USE_PHYSICS;USE_CRYPTO;%(PreprocessorDefinitions) + true + MultiThreadedDLL + false + + + + + .\Release\x32\openwolf.pch + .\Release\x32\ + .\Release\x32\ + .\Release\x32\ + Level4 + true + ..\libs\libtheora\include;..\libs\libvorbis\include;..\libs\openal\include;..\libs\mysql\include;..\libs\cpuinfo;..\libs\libspeex\include;..\libs\xvidcore\include;..\libs\zlibwapi\include;..\libs\libsdl\include;..\libs\libpng;..\libs\glew\include;..\libs\ruby\lib\ruby\1.8\i386-mswin32;..\libs\openssl\include;..\libs\curl-7.21.6\include;..\libs\libogg\include;..\libs\libspeex\libspeex;..\libs\llvm\include;..\libs\bass\include;..\libs\libbz2\include;..\libs\libnewton\include;..\libs\libnewton\include\dContainers;..\libs\libnewton\include\dMath;..\libs\msinttypes;..\libs\gmp\include;..\libs\nettle\;%(AdditionalIncludeDirectories) + StreamingSIMDExtensions + Default + ProgramDatabase + 16Bytes + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + sys\winquake.res + + + + + + + + + /MACHINE:I386 %(AdditionalOptions) + dinput8.lib;winmm.lib;wsock32.lib;Iphlpapi.lib;libcurl_imp.lib;ws2_32.lib;glu32.lib;libmysql.lib;xvidcore.lib;psapi.lib;opengl32.lib;SDL.lib;SDLMain.lib;newton.lib;glew32.lib;CPUInfo.lib;advapi32.lib;GlU32.Lib;libeay32.lib;zlibwapi.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;libtheora.lib;OpenAL32.lib;bass.lib;libbz2.lib;libgmp.a;%(AdditionalDependencies) + ../../bin/win32/release/OpenWolf.exe + true + ..\libs\libvorbis\libs\win32\Release;..\libs\curl-7.21.6\lib\win32\release;..\libs\mysql\lib\win32;..\libs\xvidcore\lib;..\libs\libsdl\libs\x32;..\libs\zlibwapi\lib\x32;..\libs\glew\lib\x32;..\libs\cpuinfo\lib\x32\release;..\libs\ruby\lib;..\libs\openssl\libs\win32;..\libs\libogg\libs\win32\release;..\libs\libvorbis\libs\Win32\release;..\libs\libtheora\libs\win32\release;..\libs\Ruby\lib;..\libs\openal\libs\win32;..\libs\bass\libs\win32;..\libs\libbz2\libs\x32;..\libs\libnewton\libs\win32;..\libs\gmp\libs\win32;%(AdditionalLibraryDirectories) + ../../OpenWolf.intermediate.manifest + libcmt.lib;%(IgnoreSpecificDefaultLibraries) + + + + + + + + + Console + 8388608 + false + + + + + + + + + + + + + + + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + .\Release/wolf.tlb + + + OnlyExplicitInline + WIN32;AMD64;WIN64;__WIN64__;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;USE_CODEC_VORBIS;USE_OPENAL;USE_OPENAL_DLOPEN;USE_LOCAL_HEADERS;USE_CIN_THEORA;HAVE_CONFIG_H;USE_VOIP;USE_MUMBLE;USE_BASS;USE_PHYSICS;USE_OPENSSL;USE_CRYPTO;USE_PHYSICS;%(PreprocessorDefinitions) + true + MultiThreadedDLL + false + true + + + .\Release/wolf.pch + .\Release/ + .\Release/ + .\Release/ + Level4 + true + ..\libs\libtheora\include;..\libs\libvorbis\include;..\libs\openal\include;..\libs\mysql\include;..\libs\cpuinfo;..\libs\libspeex\include;..\libs\zlibwapi\include;..\libs\libsdl\include;..\libs\libpng;..\libs\glew\include;..\libs\openssl\include;..\libs\curl-7.21.6\include;..\libs\libogg\include;..\libs\libspeex\libspeex;..\libs\bass\include;..\libs\libbz2\include;..\libs\msinttypes;..\libs\libnewton\include;..\libs\libnewton\include\dContainers;..\libs\libnewton\include\dMath;..\libs\nettle;..\libs\gmp\include;%(AdditionalIncludeDirectories) + StreamingSIMDExtensions + Default + ProgramDatabase + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + + + + + + + dinput8.lib;winmm.lib;wsock32.lib;Iphlpapi.lib;libcurl_imp.lib;ws2_32.lib;glu32.lib;libmysql.lib;psapi.lib;opengl32.lib;SDL.lib;SDLMain.lib;newton.lib;glew64.lib;CPUInfo.lib;advapi32.lib;GlU32.Lib;libeay32.lib;zlibwapi.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;libtheora.lib;OpenAL32.lib;bass.lib;libbz2.lib;libgmp.a;%(AdditionalDependencies) + ../../bin/win64/release/OpenWolf.exe + true + ..\libs\libvorbis\libs\x64\Release;..\libs\curl-7.21.6\lib\win64\release;..\libs\mysql\lib\win64;..\libs\xvidcore\lib;..\libs\libsdl\libs\x64;..\libs\zlibwapi\lib\x64;..\libs\glew\lib\x64;..\libs\cpuinfo\lib\x64\release;..\libs\openssl\libs\win64;..\libs\libogg\libs\win64\release;..\libs\libtheora\libs\win64\release;..\libs\openal\libs\win64;.\botlib\Release\x64;..\libs\bass\libs\win64;..\libs\libbz2\libs\x64;..\libs\libnewton\libs\win64;..\libs\gmp\libs\win64;%(AdditionalLibraryDirectories) + ../../OpenWolf_x64.intermediate.manifest + + + + + + + + + + + Console + 8388608 + false + + + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release_Dedicated/wolf.tlb + + + OnlyExplicitInline + WIN32;NDEBUG;_WINDOWS;DEDICATED;_CRT_SECURE_NO_WARNINGS;USE_INCREASED_ENTITIES;ZLIB_WINAPI;BOTLIB;USE_PHYSICS;USE_CRYPTO;%(PreprocessorDefinitions) + true + MultiThreadedDLL + true + + + .\Release_Dedicated\x32\OpenWolf_x32.pch + .\Release_Dedicated\x32\ + .\Release_Dedicated\x32\ + .\Release_Dedicated\x32\ + Level4 + true + ..\libs\libtheora\include;..\libs\libvorbis\include;..\libs\openal\include;..\libs\mysql\include;..\libs\cpuinfo;..\libs\libspeex\include;..\libs\xvidcore\include;..\libs\zlibwapi\include;..\libs\libsdl\include;..\libs\libpng;..\libs\glew\include;..\libs\ruby\lib\ruby\1.8\i386-mswin32;..\libs\openssl\include;..\libs\curl-7.21.6\include;..\libs\libogg\include;..\libs\libspeex\libspeex;..\libs\libnewton\include;..\libs\libnewton\include\dContainers;..\libs\libnewton\include\dMath;..\libs\msinttypes;..\libs\gmp\include;..\libs\nettle\;%(AdditionalIncludeDirectories) + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /MACHINE:I386 %(AdditionalOptions) + dinput8.lib;winmm.lib;wsock32.lib;Iphlpapi.lib;libcurl_imp.lib;ws2_32.lib;glu32.lib;libmysql.lib;xvidcore.lib;psapi.lib;opengl32.lib;SDL.lib;SDLMain.lib;newton.lib;glew32.lib;CPUInfo.lib;advapi32.lib;GlU32.Lib;msvcrt-ruby18.lib;zlibwapi.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;libspeex.lib;libspeexdsp.lib;libtheora.lib;OpenAL32.lib;botlib.lib;libeay32.lib;libgmp.a;%(AdditionalDependencies) + ../../bin/win32/release/OpenWolf-dedicated.exe + true + ../../bin32/release/OpenWolf_x32-dedicated.intermediate.manifest + libcmtd.lib;%(IgnoreSpecificDefaultLibraries) + + + + + + + + + Console + 8388608 + false + + + ..\libs\libvorbis\libs\win32\Release;..\libs\curl-7.21.6\lib\win32\release;..\libs\mysql\lib\win32;..\libs\xvidcore\lib;..\libs\libsdl\libs\x32;..\libs\zlibwapi\lib\x32;..\libs\glew\lib\x32;..\libs\cpuinfo\lib\x32\release;..\libs\ruby\lib;..\libs\openssl\libs\win32;..\libs\libogg\libs\win32\release;..\libs\libvorbis\libs\Win32\release;..\libs\libspeex\libs\win32\release;..\libs\libtheora\libs\win32\release;..\libs\Ruby\lib;..\libs\openal\libs\win32;.\botlib\Release\x32;..\libs\libnewton\libs\win32\static;..\libs\gmp\libs\win32;%(AdditionalLibraryDirectories) + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + .\Release_Dedicated/wolf.tlb + + + OnlyExplicitInline + WIN32;AMD64;WIN64;__WIN64__;NDEBUG;_WINDOWS;DEDICATED;_CRT_SECURE_NO_WARNINGS;USE_LOCAL_HEADERS;HAVE_CONFIG_H;USE_PHYSICS;USE_CRYPTO;%(PreprocessorDefinitions) + true + MultiThreadedDLL + true + + + .\Release_Dedicated\x64\OpenWolf_x64.pch + .\Release_Dedicated\x64\ + .\Release_Dedicated\x64\ + .\Release_Dedicated\x64\ + Level4 + true + ..\libs\libtheora\include;..\libs\libvorbis\include;..\libs\openal\include;..\libs\mysql\include;..\libs\cpuinfo;..\libs\libspeex\include;..\libs\zlibwapi\include;..\libs\libsdl\include;..\libs\libpng;..\libs\glew\include;..\libs\openssl\include;..\libs\curl-7.21.6\include;..\libs\libogg\include;..\libs\libspeex\libspeex;..\libs\libnewton\include;..\libs\libnewton\include\dContainers;..\libs\libnewton\include\dMath;..\libs\msinttypes;..\libs\gmp\include;..\libs\nettle;%(AdditionalIncludeDirectories) + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + dinput8.lib;winmm.lib;wsock32.lib;Iphlpapi.lib;libcurl_imp.lib;ws2_32.lib;glu32.lib;libmysql.lib;psapi.lib;opengl32.lib;SDL.lib;SDLMain.lib;newton.lib;glew64.lib;CPUInfo.lib;advapi32.lib;GlU32.Lib;zlibwapi.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;libspeex.lib;libspeexdsp.lib;libtheora.lib;OpenAL32.lib;botlib.lib;libeay32.lib;libgmp.a;%(AdditionalDependencies) + ../../bin/win64/release/OpenWolf-dedicated.exe + true + ../../bin64/release/OpenWolf_x64-dedicated.intermediate.manifest + libcmtd.lib;%(IgnoreSpecificDefaultLibraries) + + + + + + + + + Console + 8388608 + false + + + ..\libs\libvorbis\libs\x64\Release;..\libs\curl-7.21.6\lib\win64\release;..\libs\mysql\lib\win64;..\libs\xvidcore\lib;..\libs\libsdl\libs\x64;..\libs\newton-dynamics-2.33\libs\windows\x64;..\libs\zlibwapi\lib\x64;..\libs\glew\lib\x64;..\libs\cpuinfo\lib\x64\release;..\libs\openssl\lib\VC;..\libs\libogg\libs\win64\release;..\libs\libspeex\libs\win64\release;..\libs\libtheora\libs\win64\release;..\libs\openal\libs\win64;.\botlib\Release\x64;..\libs\libnewton\libs\win64\static;..\libs\openssl\libs\win64;..\libs\gmp\libs\win64;%(AdditionalLibraryDirectories) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + + + true + true + true + true + + + true + true + true + true + true + true + true + true + + + + + true + true + true + true + + + true + true + true + true + + + + + + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + true + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + true + true + + + true + true + + + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + ml -c -Zi "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + ml64 -D idx64=1 -c -Zi "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + ml -c "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + $(IntDir)%(FileName).obj;%(Outputs) + Assembling... + ml64 -D idx64=1 -c -Zi "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + ml -c -Zi "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + ml -c "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + ml64 -D idx64=1 -c -Zi "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + ml64 -D idx64=1 -c -Zi "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + + + Document + ml -c -Zi "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + ml64 -D idx64=1 -c -Zi "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + ml -c "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + $(IntDir)%(FileName).obj;%(Outputs) + Assembling... + ml64 -D idx64=1 -c "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + ml -c -Zi "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + ml -c "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + ml64 -D idx64=1 -c "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + ml64 -D idx64=1 -c -Zi "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + + + Document + true + ml64 -D idx64=1 -c -Zi "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + true + ml64 -D idx64=1 -c "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + true + ml64 -D idx64=1 -c "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + ml64 -D idx64=1 -c -Zi "-Fl$(IntDir)%(FileName).lst" "-Fo$(IntDir)%(FileName).obj" "%(FullPath)" + Assembling... + $(IntDir)%(FileName).obj;%(Outputs) + + + + + + + + {efa65954-05a8-43c4-b4fe-fafe8e678ab7} + + + + + + \ No newline at end of file diff --git a/src/engine/OpenWolf.vcxproj.filters b/src/engine/OpenWolf.vcxproj.filters new file mode 100644 index 0000000000..9f7d80d35b --- /dev/null +++ b/src/engine/OpenWolf.vcxproj.filters @@ -0,0 +1,1013 @@ + + + + + {4c89945e-243c-4dc0-a181-678f3ed97198} + h;hpp;hxx;hm;inl;fi;fd + + + {7b0b64e8-4593-4846-83ba-6f31b9d369d8} + + + {5ea3b017-6857-4adc-a083-62a3d3da66c9} + + + {c075986d-d31f-4263-be9d-1ac391e15cca} + + + {ce19da42-1e46-416d-9ffa-22a91dd418b0} + + + {7bf922f2-cc66-4311-a318-9edd22426969} + + + {f25fdf5a-3030-419b-b87c-a09be014f100} + + + {db53e288-bca5-47bb-a16a-1e911b97ea89} + + + {ef899b7f-4921-4cfe-a42c-1f46da3b7be9} + + + {4b31b657-b542-4873-8c7b-b2c0cf758a5f} + + + {595aea60-2f66-4e53-9d27-7df54408b87e} + + + {c3d65011-0ca1-4d49-a1c0-620ac3bfe0e6} + + + {318b9ef7-b41b-4509-95f1-50fe88195d63} + + + {35ea9957-d9b9-4a99-8dcc-989fa896d7fb} + + + {ecd2925d-543e-4c50-b82c-3716f9649bab} + cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90 + + + {16d48670-7bc1-4b64-936b-45f6967d26f2} + + + {2e422aad-249c-4606-abf2-c76e86d9095e} + + + {88708243-5a8b-49d8-aa2a-c3e91ef2cef7} + + + {38872ad7-7f86-431e-b2df-cbf3f6d15ea8} + + + {a6e49cd3-6c48-414c-bef8-53e08024929a} + + + {3dd61936-12bc-4ef7-ada2-d03bfccebc21} + + + {8e1bd649-4fb4-4660-90a4-eafdabe79f5e} + + + {0ac26362-b3b8-4d0a-9869-5bcde31d1601} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + download + + + download + + + download + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + libs\Newton\dMath\Source Files + + + libs\Newton\dMath\Source Files + + + libs\Newton\dMath\Source Files + + + libs\Newton\dMath\Source Files + + + libs\Newton\dMath\Source Files + + + libs\Newton\dContainers\Source Files + + + libs\Newton\dContainers\Source Files + + + libs\Newton\dContainers\Source Files + + + libs\Newton\dContainers\Source Files + + + libs\Newton\dContainers\Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + libs\Nettle\Source Files + + + libs\Nettle\Source Files + + + libs\Nettle\Source Files + + + libs\Nettle\Source Files + + + libs\Nettle\Source Files + + + libs\Nettle\Source Files + + + libs\Nettle\Source Files + + + libs\Nettle\Source Files + + + libs\Nettle\Source Files + + + libs\Nettle\Source Files + + + libs\Nettle\Source Files + + + libs\Nettle\Source Files + + + libs\Nettle\Source Files + + + Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + libs\Speex\Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + main\ui + + + download + + + download + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + libs\Newton + + + libs\Newton\dMath\Header Files + + + libs\Newton\dMath\Header Files + + + libs\Newton\dMath\Header Files + + + libs\Newton\dMath\Header Files + + + libs\Newton\dMath\Header Files + + + libs\Newton\dContainers\Header Files + + + libs\Newton\dContainers\Header Files + + + libs\Newton\dContainers\Header Files + + + libs\Newton\dContainers\Header Files + + + libs\Newton\dContainers\Header Files + + + libs\Newton\dContainers\Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + libs\Nettle\Header Files + + + libs\Nettle\Header Files + + + libs\Nettle\Header Files + + + Header Files + + + libs\Nettle\Header Files + + + libs\GMP\Header Files + + + libs\Speex\Header Files + + + Header Files + + + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\ui + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + main\scripts + + + + + Resource Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/src/engine/asm/ftola.asm b/src/engine/asm/ftola.asm new file mode 100644 index 0000000000..48821cc04c --- /dev/null +++ b/src/engine/asm/ftola.asm @@ -0,0 +1,90 @@ +; =========================================================================== +; Copyright (C) 2011 Thilo Schulz +; +; This file is part of OpenWolf source code. +; +; OpenWolf 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. +; +; OpenWolf 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 OpenWolf source code; if not, write to the Free Software +; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +; =========================================================================== + +; MASM ftol conversion functions using SSE or FPU +; assume __cdecl calling convention is being used for x86, __fastcall for x64 + +IFNDEF idx64 +.model flat, c +ENDIF + +; .data + +; ifndef idx64 +; fpucw WORD 0F7Fh +; endif + +.code + +IFDEF idx64 +; qftol using SSE + + qftolsse PROC + cvttss2si eax, xmm0 + ret + qftolsse ENDP + + qvmftolsse PROC + movss xmm0, dword ptr [rdi + rbx * 4] + cvttss2si eax, xmm0 + ret + qvmftolsse ENDP + +ELSE +; qftol using FPU + + qftolx87m macro src +; not necessary, fpucw is set with _controlfp at startup +; sub esp, 2 +; fnstcw word ptr [esp] +; fldcw fpucw + fld dword ptr src + fistp dword ptr src +; fldcw [esp] + mov eax, src +; add esp, 2 + ret + endm + + qftolx87 PROC +; need this line when storing FPU control word on stack +; qftolx87m [esp + 6] + qftolx87m [esp + 4] + qftolx87 ENDP + + qvmftolx87 PROC + qftolx87m [edi + ebx * 4] + qvmftolx87 ENDP + +; qftol using SSE + qftolsse PROC + movss xmm0, dword ptr [esp + 4] + cvttss2si eax, xmm0 + ret + qftolsse ENDP + + qvmftolsse PROC + movss xmm0, dword ptr [edi + ebx * 4] + cvttss2si eax, xmm0 + ret + qvmftolsse ENDP +ENDIF + +end diff --git a/src/engine/asm/ftola.c b/src/engine/asm/ftola.c new file mode 100644 index 0000000000..29b4ee9bda --- /dev/null +++ b/src/engine/asm/ftola.c @@ -0,0 +1,88 @@ +/* +=========================================================================== +Copyright (C) 2011 Thilo Schulz + +This file is part of OpenWolf source code. + +OpenWolf 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. + +OpenWolf 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 OpenWolf source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "qasm-inline.h" + +/* + * GNU inline asm ftol conversion functions using SSE or FPU + */ + +long qftolsse(float f) +{ + long retval; + + __asm__ volatile + ( + "cvttss2si %1, %0\n" + : "=r" (retval) + : "x" (f) + ); + + return retval; +} + +int qvmftolsse(void) +{ + int retval; + + __asm__ volatile + ( + "movss (" EDI ", " EBX ", 4), %%xmm0\n" + "cvttss2si %%xmm0, %0\n" + : "=r" (retval) + : + : "%xmm0" + ); + + return retval; +} + +long qftolx87(float f) +{ + long retval; + + __asm__ volatile + ( + "flds %1\n" + "fistpl %1\n" + "mov %1, %0\n" + : "=r" (retval) + : "m" (f) + ); + + return retval; +} + +int qvmftolx87(void) +{ + int retval; + + __asm__ volatile + ( + "flds (" EDI ", " EBX ", 4)\n" + "fistpl (" EDI ", " EBX ", 4)\n" + "mov (" EDI ", " EBX ", 4), %0\n" + : "=r" (retval) + ); + + return retval; +} diff --git a/src/engine/asm/matha.s b/src/engine/asm/matha.s new file mode 100644 index 0000000000..02406ab777 --- /dev/null +++ b/src/engine/asm/matha.s @@ -0,0 +1,54 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of OpenWolf source code. + +OpenWolf 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. + +OpenWolf 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 OpenWolf source code; 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 + + .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 + +#endif // id386 diff --git a/src/engine/asm/qasm-inline.h b/src/engine/asm/qasm-inline.h new file mode 100644 index 0000000000..472a64dbe2 --- /dev/null +++ b/src/engine/asm/qasm-inline.h @@ -0,0 +1,40 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of OpenWolf source code. + +OpenWolf 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. + +OpenWolf 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 OpenWolf source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#ifndef __ASM_INLINE_I386__ +#define __ASM_INLINE_I386__ + +// There is no q_platform.h in qcommon ... which one do we want to include here ? +// include "../qcommon/q_platform.h" + +#if idx64 + #define EAX "%%rax" + #define EBX "%%rbx" + #define ESP "%%rsp" + #define EDI "%%rdi" +#else + #define EAX "%%eax" + #define EBX "%%ebx" + #define ESP "%%esp" + #define EDI "%%edi" +#endif + +#endif diff --git a/src/engine/asm/qasm.h b/src/engine/asm/qasm.h new file mode 100644 index 0000000000..569e4c7a9b --- /dev/null +++ b/src/engine/asm/qasm.h @@ -0,0 +1,37 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of OpenWolf source code. + +OpenWolf 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. + +OpenWolf 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 OpenWolf 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 + +#if defined(__ELF__) || defined(__WIN64__) +#define C(label) label +#else +#define C(label) _##label +#endif + +#endif diff --git a/src/engine/asm/snapvector.asm b/src/engine/asm/snapvector.asm new file mode 100644 index 0000000000..5477154e1d --- /dev/null +++ b/src/engine/asm/snapvector.asm @@ -0,0 +1,107 @@ +; =========================================================================== +; Copyright (C) 2011 Thilo Schulz +; +; This file is part of OpenWolf source code. +; +; OpenWolf 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. +; +; OpenWolf 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 OpenWolf source code; if not, write to the Free Software +; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +; =========================================================================== + +; MASM version of snapvector conversion function using SSE or FPU +; assume __cdecl calling convention is being used for x86, __fastcall for x64 +; +; function prototype: +; void qsnapvector(vec3_t vec) + +IFNDEF idx64 +.model flat, c +ENDIF + +.data + + ALIGN 16 + ssemask DWORD 0FFFFFFFFh, 0FFFFFFFFh, 0FFFFFFFFh, 00000000h + ssecw DWORD 00001F80h + +IFNDEF idx64 + fpucw WORD 037Fh +ENDIF + +.code + +IFDEF idx64 +; qsnapvector using SSE + + qsnapvectorsse PROC + sub rsp, 8 + stmxcsr [rsp] ; save SSE control word + ldmxcsr ssecw ; set to round nearest + + push rdi + mov rdi, rcx ; maskmovdqu uses rdi as implicit memory operand + movaps xmm1, ssemask ; initialize the mask register for maskmovdqu + movups xmm0, [rdi] ; here is stored our vector. Read 4 values in one go + cvtps2dq xmm0, xmm0 ; convert 4 single fp to int + cvtdq2ps xmm0, xmm0 ; convert 4 int to single fp + maskmovdqu xmm0, xmm1 ; write 3 values back to memory + pop rdi + + ldmxcsr [rsp] ; restore sse control word to old value + add rsp, 8 + ret + qsnapvectorsse ENDP + +ELSE + + qsnapvectorsse PROC + sub esp, 8 + stmxcsr [esp] ; save SSE control word + ldmxcsr ssecw ; set to round nearest + + push edi + mov edi, dword ptr 16[esp] ; maskmovdqu uses edi as implicit memory operand + movaps xmm1, ssemask ; initialize the mask register for maskmovdqu + movups xmm0, [edi] ; here is stored our vector. Read 4 values in one go + cvtps2dq xmm0, xmm0 ; convert 4 single fp to int + cvtdq2ps xmm0, xmm0 ; convert 4 int to single fp + maskmovdqu xmm0, xmm1 ; write 3 values back to memory + pop edi + + ldmxcsr [esp] ; restore sse control word to old value + add esp, 8 + ret + qsnapvectorsse ENDP + + qroundx87 macro src + fld dword ptr src + fistp dword ptr src + fild dword ptr src + fstp dword ptr src + endm + + qsnapvectorx87 PROC + mov eax, dword ptr 4[esp] + sub esp, 2 + fnstcw word ptr [esp] + fldcw fpucw + qroundx87 [eax] + qroundx87 4[eax] + qroundx87 8[eax] + fldcw [esp] + add esp, 2 + qsnapvectorx87 ENDP + +ENDIF + +end diff --git a/src/engine/asm/snapvector.c b/src/engine/asm/snapvector.c new file mode 100644 index 0000000000..eef138c29e --- /dev/null +++ b/src/engine/asm/snapvector.c @@ -0,0 +1,87 @@ +/* +=========================================================================== +Copyright (C) 2011 Thilo Schulz + +This file is part of OpenWolf source code. + +OpenWolf 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. + +OpenWolf 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 OpenWolf source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "qasm-inline.h" +#include "../qcommon/q_shared.h" + +/* + * GNU inline asm version of qsnapvector + * See MASM snapvector.asm for commentary + */ + +static unsigned char ssemask[16] __attribute__((aligned(16))) = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00" +}; + +static const unsigned int ssecw __attribute__((aligned(16))) = 0x00001F80; +static const unsigned short fpucw = 0x037F; + +void qsnapvectorsse(vec3_t vec) +{ + uint32_t oldcw __attribute__((aligned(16))); + + __asm__ volatile + ( + "stmxcsr %3\n" + "ldmxcsr %1\n" + + "movaps (%0), %%xmm1\n" + "movups (%2), %%xmm0\n" + "cvtps2dq %%xmm0, %%xmm0\n" + "cvtdq2ps %%xmm0, %%xmm0\n" + // vec MUST reside in register rdi as maskmovdqu uses + // it as an implicit operand. The "D" constraint makes + // sure of that. + "maskmovdqu %%xmm1, %%xmm0\n" + + "ldmxcsr %3\n" + : + : "r" (ssemask), "m" (ssecw), "D" (vec), "m" (oldcw) + : "memory", "%xmm0", "%xmm1" + ); + +} + +#define QROUNDX87(src) \ + "flds " src "\n" \ + "fistps " src "\n" \ + "filds " src "\n" \ + "fstps " src "\n" + +void qsnapvectorx87(vec3_t vec) +{ + __asm__ volatile + ( + "sub $2, " ESP "\n" + "fnstcw (" ESP ")\n" + "fldcw %0\n" + QROUNDX87("(%1)") + QROUNDX87("4(%1)") + QROUNDX87("8(%1)") + "fldcw (" ESP ")\n" + "add $2, " ESP "\n" + : + : "m" (fpucw), "r" (vec) + : "memory" + ); +} diff --git a/src/engine/asm/snd_mixa.s b/src/engine/asm/snd_mixa.s new file mode 100644 index 0000000000..d4d92005d0 --- /dev/null +++ b/src/engine/asm/snd_mixa.s @@ -0,0 +1,217 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of OpenWolf source code. + +OpenWolf 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. + +OpenWolf 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 OpenWolf source code; 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/src/engine/asm/vm_x86_64.asm b/src/engine/asm/vm_x86_64.asm new file mode 100644 index 0000000000..59a81089b0 --- /dev/null +++ b/src/engine/asm/vm_x86_64.asm @@ -0,0 +1,76 @@ +; =========================================================================== +; Copyright (C) 2011 Thilo Schulz +; +; This file is part of OpenWolf source code. +; +; OpenWolf 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. +; +; OpenWolf 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 OpenWolf source code; if not, write to the Free Software +; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +; =========================================================================== + +; Call wrapper for vm_x86 when built with MSVC in 64 bit mode, +; since MSVC does not support inline x64 assembler code anymore. +; +; assumes __fastcall calling convention + +DoSyscall PROTO + +.code + +; Call to static void DoSyscall(int syscallNum, int programStack, int *opStackBase, uint8_t opStackOfs, intptr_t arg) + +qsyscall64 PROC + sub rsp, 28h ; after this esp will be aligned to 16 byte boundary + mov qword ptr [rsp + 20h], rcx ; 5th parameter "arg" is passed on stack + mov r9b, bl ; opStackOfs + mov r8, rdi ; opStackBase + mov edx, esi ; programStack + mov ecx, eax ; syscallNum + mov rax, DoSyscall ; store call address of DoSyscall in rax + call rax + add rsp, 28h + ret +qsyscall64 ENDP + + +; Call to compiled code after setting up the register environment for the VM +; prototype: +; uint8_t qvmcall64(int *programStack, int *opStack, intptr_t *instructionPointers, byte *dataBase); + +qvmcall64 PROC + push rsi ; push non-volatile registers to stack + push rdi + push rbx + ; need to save pointer in rcx so we can write back the programData value to caller + push rcx + + ; registers r8 and r9 have correct value already thanx to __fastcall + xor rbx, rbx ; opStackOfs starts out being 0 + mov rdi, rdx ; opStack + mov esi, dword ptr [rcx] ; programStack + + call qword ptr [r8] ; instructionPointers[0] is also the entry point + + pop rcx + + mov dword ptr [rcx], esi ; write back the programStack value + mov al, bl ; return opStack offset + + pop rbx + pop rdi + pop rsi + + ret +qvmcall64 ENDP + +end diff --git a/src/engine/botlib/aasfile.h b/src/engine/botlib/aasfile.h new file mode 100644 index 0000000000..64f34574f8 --- /dev/null +++ b/src/engine/botlib/aasfile.h @@ -0,0 +1,291 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + + +//NOTE: int = default signed +// default long + +#define AASID ( ( 'S' << 24 ) + ( 'A' << 16 ) + ( 'A' << 8 ) + 'E' ) +#define AASVERSION 8 + +//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 +#define FACE_LIQUIDSURFACE 32 + +//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 +// Rafael - nopass +#define AREACONTENTS_DONOTENTER_LARGE 1024 +#define AREACONTENTS_MOVER 2048 + +//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 +// Ridah +#define AREA_DISABLED 8 +#define AREA_AVOID 16 +#define AREA_TEAM_AXIS 32 +#define AREA_TEAM_ALLIES 64 +#define AREA_TEAM_AXIS_DISGUISED 128 +#define AREA_TEAM_ALLIES_DISGUISED 256 +#define AREA_USEFORROUTING 1024 +#define AREA_AVOID_AXIS 2048 // death area +#define AREA_AVOID_ALLIES 4096 // death area + +#define AREA_TEAM_FLAGS ( AREA_TEAM_AXIS | AREA_TEAM_ALLIES | AREA_TEAM_AXIS_DISGUISED | AREA_TEAM_ALLIES_DISGUISED | AREA_AVOID_AXIS | AREA_AVOID_ALLIES ) + +//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 convex area + int areaflags; //several area flags + int presencetype; //how a bot can be present in this convex 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 + // Ridah, add a ground steepness stat, so we can avoid terrain when we can take a close-by flat route + float groundsteepness; // 0 = flat, 1 = steep +} 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 a convex area, often it will also seperate two convex 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; //convex area at the front of this face + int backarea; //convex 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; + +//convex 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 convex area + int firstface; //first face in the face index used for the boundary of the convex area + vec3_t mins; //mins of the convex area + vec3_t maxs; //maxs of the convex area + vec3_t center; //'center' of the convex 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 convex 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 convex 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 + cluster number zero +- edge zero is a dummy +- face zero is a dummy +- area zero is a dummy +- node zero is a dummy +*/ diff --git a/src/engine/botlib/be_aas.h b/src/engine/botlib/be_aas.h new file mode 100644 index 0000000000..d4c4bf2877 --- /dev/null +++ b/src/engine/botlib/be_aas.h @@ -0,0 +1,253 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas.h + * + * desc: Area Awareness System, stuff exported to the AI + * + * + *****************************************************************************/ + +#ifndef MAX_AAS_WORLDS +#define MAX_AAS_WORLDS 1 // one for each bounding box type + +#ifndef MAX_STRINGFIELD +#define MAX_STRINGFIELD 80 +#endif + +//travel flags +#define TFL_INVALID 0x0000001 //traveling temporary not possible +#define TFL_WALK 0x0000002 //walking +#define TFL_CROUCH 0x0000004 //crouching +#define TFL_BARRIERJUMP 0x0000008 //jumping onto a barrier +#define TFL_JUMP 0x0000010 //jumping +#define TFL_LADDER 0x0000020 //climbing a ladder +#define TFL_WALKOFFLEDGE 0x0000080 //walking of a ledge +#define TFL_SWIM 0x0000100 //swimming +#define TFL_WATERJUMP 0x0000200 //jumping out of the water +#define TFL_TELEPORT 0x0000400 //teleporting +#define TFL_ELEVATOR 0x0000800 //elevator +#define TFL_ROCKETJUMP 0x0001000 //rocket jumping +#define TFL_BFGJUMP 0x0002000 //bfg jumping +#define TFL_GRAPPLEHOOK 0x0004000 //grappling hook +#define TFL_DOUBLEJUMP 0x0008000 //double jump +#define TFL_RAMPJUMP 0x0010000 //ramp jump +#define TFL_STRAFEJUMP 0x0020000 //strafe jump +#define TFL_JUMPPAD 0x0040000 //jump pad +#define TFL_AIR 0x0080000 //travel through air +#define TFL_WATER 0x0100000 //travel through water +#define TFL_SLIME 0x0200000 //travel through slime +#define TFL_LAVA 0x0400000 //travel through lava +#define TFL_DONOTENTER 0x0800000 //travel through donotenter area +#define TFL_FUNCBOB 0x1000000 //func bobbing +#define TFL_DONOTENTER_LARGE 0x2000000 //travel through donotenter area +#define TFL_TEAM_AXIS 0x4000000 //travel through axis-only areas +#define TFL_TEAM_ALLIES 0x8000000 //travel through allies-only areas +#define TFL_TEAM_AXIS_DISGUISED 0x10000000 //travel through axis+DISGUISED areas +#define TFL_TEAM_ALLIES_DISGUISED 0x2000000 //travel through allies+DISGUISED areas + +#define TFL_TEAM_FLAGS ( TFL_TEAM_AXIS | TFL_TEAM_ALLIES | TFL_TEAM_AXIS_DISGUISED | TFL_TEAM_ALLIES_DISGUISED ) + +//default travel flags + +//----(SA) modified since slime is no longer deadly +#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_SLIME | \ + 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 +// int weapAnim; // mask off ANIM_TOGGLEBIT //----(SA) added +//----(SA) didn't want to comment in as I wasn't sure of any implications of changing this structure. +} aas_entityinfo_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_HITENT 2048 // hit specified entity +#define SE_STUCK 4096 + +#ifndef BSPTRACE + +//bsp_trace_t hit surface +typedef struct bsp_surface_s +{ + char name[16]; + int flags; + int value; +} bsp_surface_t; + +#ifndef CPLANE +// plane_t structure +// !!! if this is changed, it must be changed in asm code too !!! +typedef struct +{ + vec3_t normal; + float dist; + byte type; // for fast side tests: 0,1,2 = axial, 3 = nonaxial + byte signbits; // signx + (signy<<1) + (signz<<2), used as lookup during collision + byte pad[2]; +} cplane_t; + +#define CPLANE +#endif + +//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; + +#define BSPTRACE +#endif // BSPTRACE + +typedef struct aas_clientmove_s +{ + vec3_t endpos; //position at the end of movement prediction + vec3_t velocity; //velocity at the end of movement prediction + struct bsp_trace_s trace; //last trace + int presencetype; //presence type at end of movement prediction + int stopevent; //event that made the prediction stop + float endcontents; //contents at the end of movement prediction + float time; //time predicted ahead + int frames; //number of frames predicted ahead +} aas_clientmove_t; + +typedef struct aas_altroutegoal_s +{ + vec3_t origin; + int areanum; + unsigned short starttraveltime; + unsigned short goaltraveltime; + unsigned short extratraveltime; +} aas_altroutegoal_t; + +#endif diff --git a/src/engine/botlib/be_aas_bsp.h b/src/engine/botlib/be_aas_bsp.h new file mode 100644 index 0000000000..eb7d623668 --- /dev/null +++ b/src/engine/botlib/be_aas_bsp.h @@ -0,0 +1,103 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_bsp.h + * + * desc: AAS + * + * + *****************************************************************************/ + +#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/src/engine/botlib/be_aas_bspq3.c b/src/engine/botlib/be_aas_bspq3.c new file mode 100644 index 0000000000..ee985628e0 --- /dev/null +++ b/src/engine/botlib/be_aas_bspq3.c @@ -0,0 +1,597 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_bspq3.c + * + * desc: BSP, Environment Sampling + * + * + *****************************************************************************/ + +#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 4096 + +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]; + //memory used for strings and epairs + byte *ebuffer; +} 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) + { + 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) +{ +// RF, optimized memory allocation +/* + 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 +*/ + if(bspworld.ebuffer) + { + FreeMemory(bspworld.ebuffer); + } + 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; + byte *buffer, *buftrav; + int bufsize; + + // RF, modified this, so that it first gathers up memory requirements, then allocates a single chunk, + // and places the strings all in there + + bspworld.ebuffer = NULL; + + script = LoadScriptMemory(bspworld.dentdata, bspworld.entdatasize, "entdata"); + SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES | SCFL_NOSTRINGESCAPECHARS); //SCFL_PRIMITIVE); + + bufsize = 0; + + 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 + while(PS_ReadToken(script, &token)) + { + if(!strcmp(token.string, "}")) + { + break; + } + bufsize += sizeof(bsp_epair_t); + if(token.type != TT_STRING) + { + ScriptError(script, "invalid %s\n", token.string); + AAS_FreeBSPEntities(); + FreeScript(script); + return; + } //end if + StripDoubleQuotes(token.string); + bufsize += strlen(token.string) + 1; + if(!PS_ExpectTokenType(script, TT_STRING, 0, &token)) + { + AAS_FreeBSPEntities(); + FreeScript(script); + return; + } //end if + StripDoubleQuotes(token.string); + bufsize += strlen(token.string) + 1; + } //end while + if(strcmp(token.string, "}")) + { + ScriptError(script, "missing }\n"); + AAS_FreeBSPEntities(); + FreeScript(script); + return; + } //end if + } //end while + FreeScript(script); + + buffer = (byte *) GetClearedHunkMemory(bufsize); + buftrav = buffer; + bspworld.ebuffer = buffer; + + // RF, now parse the entities into memory + // RF, NOTE: removed error checks for speed, no need to do them twice + + script = LoadScriptMemory(bspworld.dentdata, bspworld.entdatasize, "entdata"); + SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES | SCFL_NOSTRINGESCAPECHARS); //SCFL_PRIMITIVE); + + bspworld.numentities = 1; + + while(PS_ReadToken(script, &token)) + { + ent = &bspworld.entities[bspworld.numentities]; + bspworld.numentities++; + ent->epairs = NULL; + while(PS_ReadToken(script, &token)) + { + if(!strcmp(token.string, "}")) + { + break; + } + epair = (bsp_epair_t *) buftrav; + buftrav += sizeof(bsp_epair_t); + epair->next = ent->epairs; + ent->epairs = epair; + StripDoubleQuotes(token.string); + epair->key = (char *)buftrav; + buftrav += (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 *)buftrav; + buftrav += (strlen(token.string) + 1); + strcpy(epair->value, token.string); + } //end while + } //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; + 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); + memcpy(bspworld.dentdata, botimport.BSPEntityData(), bspworld.entdatasize); + AAS_ParseBSPEntities(); + bspworld.loaded = qtrue; + return BLERR_NOERROR; +} //end of the function AAS_LoadBSPFile + +void AAS_InitBSP(void) +{ + memset(&bspworld, 0, sizeof(bspworld)); +} diff --git a/src/engine/botlib/be_aas_cluster.c b/src/engine/botlib/be_aas_cluster.c new file mode 100644 index 0000000000..fa40ba0a95 --- /dev/null +++ b/src/engine/botlib/be_aas_cluster.c @@ -0,0 +1,1761 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_cluster.c + * + * desc: area clustering + * + * + *****************************************************************************/ + +#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 + { + Log_Write("portal using area %d is seperating more than two clusters\r\n", areanum); + //remove the cluster portal flag contents + (*aasworld).areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; + 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 + } + //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; + Log_Write("portal %d: area %d\r\n", (*aasworld).numportals, portal->areanum); + (*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; + + 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; + } + // + memset(numareafrontfaces, 0, sizeof(numareafrontfaces)); + 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 portals\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 + +/* +//=========================================================================== +// +// 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*/ +//=========================================================================== +// +// 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) + { + num++; + } //end if + } //end for + botimport.Print(PRT_MESSAGE, "%6d forced portals\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((*aasworld).clusterTeamTravelFlags) + { + FreeMemory((*aasworld).clusterTeamTravelFlags); + } + (*aasworld).clusterTeamTravelFlags = (int *)GetClearedMemory((*aasworld).numclusters * sizeof(int)); + + //if clustering isn't forced + if(!((int)LibVarGetValue("forceclustering")) && !((int)LibVarGetValue("forcereachability"))) + { + return; + } +#else + return; +#endif + } //end if + // + AAS_CountForcedClusterPortals(); + //remove all the existing portals + //AAS_RemoveAllPortals(); + //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)); + // + if((*aasworld).clusterTeamTravelFlags) + { + FreeMemory((*aasworld).clusterTeamTravelFlags); + } + (*aasworld).clusterTeamTravelFlags = (int *)GetClearedMemory(AAS_MAX_CLUSTERS * sizeof(int)); + // + 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; + // 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/src/engine/botlib/be_aas_cluster.h b/src/engine/botlib/be_aas_cluster.h new file mode 100644 index 0000000000..1f68002032 --- /dev/null +++ b/src/engine/botlib/be_aas_cluster.h @@ -0,0 +1,47 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_cluster.h + * + * desc: AAS + * + * + *****************************************************************************/ + +#ifdef AASINTERN +//initialize the AAS clustering +void AAS_InitClustering(void); +#endif //AASINTERN diff --git a/src/engine/botlib/be_aas_debug.c b/src/engine/botlib/be_aas_debug.c new file mode 100644 index 0000000000..f62e592083 --- /dev/null +++ b/src/engine/botlib/be_aas_debug.c @@ -0,0 +1,813 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_debug.c + * + * desc: AAS debug code + * + * + *****************************************************************************/ + +#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 + +int debuglines[MAX_DEBUGLINES]; +int debuglinevisible[MAX_DEBUGLINES]; +int numdebuglines; + +static bot_debugpoly_t *debugpolygons[MAX_DEBUGPOLYS]; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ClearShownPolygons(void) +{ + int i; + + for(i = 0; i < MAX_DEBUGPOLYS; i++) + { + if(debugpolygons[i]) + { + botimport.DebugPolygonDeletePointer(debugpolygons[i]); + } + debugpolygons[i] = NULL; + } +} //end of the function AAS_ClearShownPolygons + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_debugpoly_t *AAS_GetDebugPolygon(void) +{ + int i; + + for(i = 0; i < MAX_DEBUGPOLYS; i++) + { + if(!debugpolygons[i]) + { + debugpolygons[i] = botimport.DebugPolygonGetFree(); + + return debugpolygons[i]; + } + } + + return NULL; +} //end of the function AAS_GetDebugPolygon + +//=========================================================================== +// +// 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 + 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; + aas_edge_t *edge; + aas_face_t *face; + + vec3_t points[128]; + int numpoints = 0; + + //check if face number is in range + if(facenum >= aasworld->numfaces) + { + botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); + } + + //walk through the edges of the face + face = &(aasworld->faces[facenum]); + + if(flip) + { + for(i = face->numedges - 1; i >= 0; i--) + { + edgenum = aasworld->edgeindex[face->firstedge + i]; + edge = &(aasworld->edges[abs(edgenum)]); + + VectorCopy(aasworld->vertexes[edge->v[edgenum < 0]], points[numpoints]); + + numpoints++; + } //end for + } + else + { + for(i = 0; i < face->numedges; i++) + { + edgenum = aasworld->edgeindex[face->firstedge + i]; + edge = &(aasworld->edges[abs(edgenum)]); + + VectorCopy(aasworld->vertexes[edge->v[edgenum < 0]], points[numpoints]); + + numpoints++; + } + } + + botimport.BotDrawPolygon(color, numpoints, (float *)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; + } + + //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); + } + + face = &(aasworld->faces[facenum]); + + //ground faces only + if(groundfacesonly) + { + if(!(face->faceflags & (FACE_GROUND | FACE_LADDER))) + { + continue; + } + } + + AAS_ShowFacePolygon(facenum, color, face->frontarea != areanum); + } +} //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) + { + 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 //DEBUG +} //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 == TRAVEL_JUMP || reach->traveltype == TRAVEL_WALKOFFLEDGE) + { + AAS_HorizontalVelocityForJump(aassettings.sv_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.sv_jumpvel; + // + AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, + velocity, cmdmove, 3, 30, 0.1, + SE_HITGROUND | SE_ENTERWATER | SE_ENTERSLIME | SE_ENTERLAVA | SE_HITGROUNDDAMAGE, 0, qtrue); + // + if(reach->traveltype == TRAVEL_JUMP) + { + AAS_JumpReachRunStart(reach, dir); + AAS_DrawCross(dir, 4, LINECOLOR_BLUE); + } //end if + } //end if + else if(reach->traveltype == 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.1, + SE_ENTERWATER | SE_ENTERSLIME | + SE_ENTERLAVA | SE_HITGROUNDDAMAGE | SE_TOUCHJUMPPAD | SE_HITGROUNDAREA, reach->areanum, qtrue); + } //end else if + else if(reach->traveltype == 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.1, + 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) + { + memcpy(&reach, &(*aasworld).reachability[settings->firstreachablearea + index], sizeof(aas_reachability_t)); + index++; + lasttime = AAS_Time(); + AAS_PrintTravelType(reach.traveltype); + botimport.Print(PRT_MESSAGE, "(traveltime: %i)\n", reach.traveltime); + } //end if + AAS_ShowReachability(&reach); +} //end of the function ShowReachableAreas diff --git a/src/engine/botlib/be_aas_debug.h b/src/engine/botlib/be_aas_debug.h new file mode 100644 index 0000000000..725b54e05d --- /dev/null +++ b/src/engine/botlib/be_aas_debug.h @@ -0,0 +1,87 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_debug.h + * + * desc: AAS + * + * + *****************************************************************************/ + +//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/src/engine/botlib/be_aas_def.h b/src/engine/botlib/be_aas_def.h new file mode 100644 index 0000000000..3786394fd8 --- /dev/null +++ b/src/engine/botlib/be_aas_def.h @@ -0,0 +1,309 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_def.h + * + * desc: AAS + * + * + *****************************************************************************/ + +//debugging on +#define AAS_DEBUG + +#define DF_AASENTNUMBER( x ) ( x - ( *aasworlds ).entities ) +#define DF_NUMBERAASENT( x ) ( &( *aasworlds ).entities[x] ) +#define DF_AASENTCLIENT( x ) ( x - ( *aasworlds ).entities - 1 ) +#define DF_CLIENTAASENT( x ) ( &( *aasworlds ).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 +{ + float sv_friction; + float sv_stopspeed; + float sv_gravity; + float sv_waterfriction; + float sv_watergravity; + float sv_maxvelocity; + float sv_maxwalkvelocity; + float sv_maxcrouchvelocity; + float sv_maxswimvelocity; + float sv_walkaccelerate; + float sv_airaccelerate; + float sv_swimaccelerate; + float sv_maxstep; + float sv_maxsteepness; + float sv_maxwaterjump; + float sv_maxbarrier; + float sv_jumpvel; + qboolean sv_allowladders; +} aas_settings_t; + +//routing cache +typedef struct aas_routingcache_s +{ + int size; //size of the routing cache + float time; //last time accessed or updated + 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; + 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; + +// Ridah, route-tables +#include "be_aas_routetable.h" +// done. + +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 reachabilityareas; + 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]; + //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; + //maximum travel time through portals + int *portalmaxtraveltimes; + // Ridah, pointer to Route-Table information + aas_rt_t *routetable; + //hide travel times + unsigned short int *hidetraveltimes; + + // Distance from Dangerous areas + unsigned short int *distanceFromDanger; + + // Priority Queue Implementation + unsigned short int *PQ_accumulator; + + // How many items are in the PQ + int PQ_size; + + //vis data + byte *decompressedvis; + int decompressedvisarea; + byte **areavisibility; + // done. + // Ridah, store the area's waypoint for hidepos calculations (center traced downwards) + vec3_t *areawaypoints; + // Ridah, so we can cache the areas that have already been tested for visibility/attackability + byte *visCache; + // RF, cluster team flags (-1 means not calculated) + int *clusterTeamTravelFlags; + // RF, last time a death influenced this area. Seperate lists for axis/allies + int *teamDeathTime; + // RF, number of deaths accumulated before the time expired + byte *teamDeathCount; + // RF, areas that are influenced by a death count + byte *teamDeathAvoid; +} 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" + +// Ridah, route-tables +#include "be_aas_routetable.h" + +#endif //BSPCINCLUDE diff --git a/src/engine/botlib/be_aas_entity.c b/src/engine/botlib/be_aas_entity.c new file mode 100644 index 0000000000..2ba6bd3dc2 --- /dev/null +++ b/src/engine/botlib/be_aas_entity.c @@ -0,0 +1,616 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_entity.c + * + * desc: AAS entities + * + * + *****************************************************************************/ + +#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 + +// Ridah, always use the default world for entities +extern aas_t aasworlds[MAX_AAS_WORLDS]; + +aas_t *defaultaasworld = aasworlds; + +//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(!(*defaultaasworld).loaded) + { + botimport.Print(PRT_MESSAGE, "AAS_UpdateEntity: not loaded\n"); + return BLERR_NOAASFILE; + } //end if + + ent = &(*defaultaasworld).entities[entnum]; + + 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; + +// ent->i.weapAnim = state->weapAnim; //----(SA) +//----(SA) didn't want to comment in as I wasn't sure of any implications of changing the aas_entityinfo_t and bot_entitystate_t structures. + + //number of the entity + ent->i.number = entnum; + //updated so set valid flag + ent->i.valid = qtrue; + //link everything the first frame + + if((*defaultaasworld).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 + + // RF, this is broken, just use the state bounds + //AAS_BSPModelMinsMaxsOrigin(ent->i.modelindex, ent->i.angles, ent->i.mins, ent->i.maxs, NULL); + VectorCopy(state->mins, ent->i.mins); + VectorCopy(state->maxs, ent->i.maxs); + } //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 + } //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) +{ + // Gordon: lets not spam this message making it impossible to see anything on the console + static qboolean debug_msg_done = qfalse; + + if(!(*defaultaasworld).initialized) + { + if(!debug_msg_done) + { + debug_msg_done = qtrue; + botimport.Print(PRT_FATAL, "AAS_EntityInfo: (*defaultaasworld) not initialized\n"); + memset(info, 0, sizeof(aas_entityinfo_t)); + } + return; + } //end if + + if(entnum < 0 || entnum >= (*defaultaasworld).maxentities) + { + // if it's not a bot game entity, then report it + if(!(entnum >= (*defaultaasworld).maxentities && entnum < (*defaultaasworld).maxentities + NUM_BOTGAMEENTITIES)) + { + botimport.Print(PRT_FATAL, "AAS_EntityInfo: entnum %d out of range\n", entnum); + } + memset(info, 0, sizeof(aas_entityinfo_t)); + return; + } //end if + + memcpy(info, &(*defaultaasworld).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 >= (*defaultaasworld).maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntityOrigin: entnum %d out of range\n", entnum); + VectorClear(origin); + return; + } //end if + + VectorCopy((*defaultaasworld).entities[entnum].i.origin, origin); +} //end of the function AAS_EntityOrigin + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_EntityModelindex(int entnum) +{ + if(entnum < 0 || entnum >= (*defaultaasworld).maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntityModelindex: entnum %d out of range\n", entnum); + return 0; + } //end if + return (*defaultaasworld).entities[entnum].i.modelindex; +} //end of the function AAS_EntityModelindex + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_EntityType(int entnum) +{ + if(!(*defaultaasworld).initialized) + { + return 0; + } + + if(entnum < 0 || entnum >= (*defaultaasworld).maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntityType: entnum %d out of range\n", entnum); + return 0; + } //end if + return (*defaultaasworld).entities[entnum].i.type; +} //end of the AAS_EntityType + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_EntityModelNum(int entnum) +{ + if(!(*defaultaasworld).initialized) + { + return 0; + } + + if(entnum < 0 || entnum >= (*defaultaasworld).maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntityModelNum: entnum %d out of range\n", entnum); + return 0; + } //end if + return (*defaultaasworld).entities[entnum].i.modelindex; +} //end of the function AAS_EntityModelNum + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_OriginOfEntityWithModelNum(int modelnum, vec3_t origin) +{ + int i; + aas_entity_t *ent; + + for(i = 0; i < (*defaultaasworld).maxentities; i++) + { + ent = &(*defaultaasworld).entities[i]; + if(ent->i.type == ET_MOVER) + { + if(ent->i.modelindex == modelnum) + { + VectorCopy(ent->i.origin, origin); + return qtrue; + } //end if + } + } //end for + return qfalse; +} //end of the function AAS_OriginOfEntityWithModelNum + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_EntitySize(int entnum, vec3_t mins, vec3_t maxs) +{ + aas_entity_t *ent; + + if(!(*defaultaasworld).initialized) + { + return; + } + + if(entnum < 0 || entnum >= (*defaultaasworld).maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntitySize: entnum %d out of range\n", entnum); + return; + } //end if + + ent = &(*defaultaasworld).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 = &(*defaultaasworld).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 < (*defaultaasworld).maxentities; i++) + { + (*defaultaasworld).entities[i].areas = NULL; + (*defaultaasworld).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 < (*defaultaasworld).maxentities; i++) + { + (*defaultaasworld).entities[i].i.valid = qfalse; + (*defaultaasworld).entities[i].i.number = i; + } //end for +} //end of the function AAS_InvalidateEntities + +//=========================================================================== +// +// 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 < (*defaultaasworld).maxentities; i++) + { + ent = &(*defaultaasworld).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 = &(*defaultaasworld).entities[entnum]; + return AAS_BestReachableLinkArea(ent->areas); +} //end of the function AAS_BestReachableEntityArea + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NextEntity(int entnum) +{ + if(!(*defaultaasworld).loaded) + { + return 0; + } + + if(entnum < 0) + { + entnum = -1; + } + while(++entnum < (*defaultaasworld).maxentities) + { + if((*defaultaasworld).entities[entnum].i.valid) + { + return entnum; + } + } //end while + return 0; +} //end of the function AAS_NextEntity + +// Ridah, used to find out if there is an entity touching the given area, if so, try and avoid it +/* +============ +AAS_EntityInArea +============ +*/ +int AAS_IsEntityInArea(int entnumIgnore, int entnumIgnore2, int areanum) +{ + aas_link_t *link; + aas_entity_t *ent; + + for(link = (*aasworld).arealinkedentities[areanum]; link; link = link->next_ent) + { + //ignore the pass entity + if(link->entnum == entnumIgnore) + { + continue; + } + if(link->entnum == entnumIgnore2) + { + continue; + } + // + ent = &(*defaultaasworld).entities[link->entnum]; + if(!ent->i.valid) + { + continue; + } + if(!ent->i.solid) + { + continue; + } + return qtrue; + } +/* + ent = (*defaultaasworld).entities; + for (i = 0; i < (*defaultaasworld).maxclients; i++, ent++) + { + if (!ent->i.valid) + continue; + if (!ent->i.solid) + continue; + if (i == entnumIgnore) + continue; + if (i == entnumIgnore2) + continue; + for (link = ent->areas; link; link = link->next_area) + { + if (link->areanum == areanum) + { + return qtrue; + } //end if + } //end for + } +*/ + return qfalse; +} + +/* +============= +AAS_SetAASBlockingEntity +============= +*/ +int AAS_EnableRoutingArea(int areanum, int enable); +void AAS_SetAASBlockingEntity(vec3_t absmin, vec3_t absmax, int blocking) +{ + int areas[1024]; + int numareas, i, w; + qboolean mover, changed = qfalse; + + // + // check for resetting AAS blocking + if(VectorCompare(absmin, absmax) && blocking < 0) + { + for(w = 0; w < MAX_AAS_WORLDS; w++) + { + AAS_SetCurrentWorld(w); + // + if(!(*aasworld).loaded) + { + continue; + } + // now clear blocking status + for(i = 1; i < (*aasworld).numareas; i++) + { + AAS_EnableRoutingArea(i, qtrue); + } + } + // + return; + } + // + if(blocking & BLOCKINGFLAG_MOVER) + { + mover = qtrue; + blocking &= ~BLOCKINGFLAG_MOVER; + } + else + { + mover = qfalse; + } + // + areas_again: + // + for(w = 0; w < MAX_AAS_WORLDS; w++) + { + AAS_SetCurrentWorld(w); + // + if(!(*aasworld).loaded) + { + continue; + } + // grab the list of areas + numareas = AAS_BBoxAreas(absmin, absmax, areas, 1024); + // now set their blocking status + for(i = 0; i < numareas; i++) + { + if(mover) + { + if(!(aasworld->areasettings[areas[i]].contents & AREACONTENTS_MOVER)) + { + continue; // this isn't a mover area, so ignore it + } + } + AAS_EnableRoutingArea(areas[i], (blocking & ~0x1) | !(blocking & 1)); + changed = qtrue; + } + } + // + if(mover && !changed) + { // map must not be compiled with MOVER flags enabled, so redo the old way + mover = qfalse; + goto areas_again; + } +} diff --git a/src/engine/botlib/be_aas_entity.h b/src/engine/botlib/be_aas_entity.h new file mode 100644 index 0000000000..163c7f3234 --- /dev/null +++ b/src/engine/botlib/be_aas_entity.h @@ -0,0 +1,86 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_entity.h + * + * desc: AAS + * + * + *****************************************************************************/ + +#ifdef AASINTERN +//invalidates all entity infos +void AAS_InvalidateEntities(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_OriginOfEntityWithModelNum(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); + +// Ridah +int AAS_IsEntityInArea(int entnumIgnore, int entnumIgnore2, int areanum); diff --git a/src/engine/botlib/be_aas_file.c b/src/engine/botlib/be_aas_file.c new file mode 100644 index 0000000000..08c894c1a8 --- /dev/null +++ b/src/engine/botlib/be_aas_file.c @@ -0,0 +1,750 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_file.c + * + * desc: AAS file loading/writing + * + * + *****************************************************************************/ + +#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; + + // Ridah, no need to do anything if this OS doesn't need byte swapping + if(LittleLong(1) == 1) + { + return; + } + // done. + + //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); + (*aasworld).areasettings[i].groundsteepness = LittleFloat((*aasworld).areasettings[i].groundsteepness); + } //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) +{ + char *buf; + + // + if(!length) + { + return NULL; + } + //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 0; + } //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; + int nocrc; + + 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) + { + AAS_Error("aas file %s is version %i, not %i\n", filename, header.version, AASVERSION); + botimport.FS_FCloseFile(fp); + return BLERR_WRONGAASFILEVERSION; + } //end if + // + //RF, checksum of -1 always passes, hack to fix commercial maps without having to distribute new bsps + nocrc = 0; + if(LittleLong(header.bspchecksum) == -1) + { + nocrc = 1; + } + // + if(header.version == AASVERSION) + { + AAS_DData((unsigned char *)&header + 8, sizeof(aas_header_t) - 8); + } //end if + // + (*aasworld).bspchecksum = atoi(LibVarGetString("sv_mapChecksum")); + if(!nocrc && 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); + (*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); + (*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); + (*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); + (*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); + (*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); + (*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); + (*aasworld).faceindexsize = length / sizeof(int); + 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); + (*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); + (*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); + (*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); + (*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); + (*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); + (*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); + (*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 + // + + { + int j = 0; + int i; + + for(i = 1; i < aasworld->numareas; i++) + { + j += aasworld->areasettings[i].numreachableareas; + } + if(j > aasworld->reachabilitysize) + { + Com_Error(ERR_DROP, "aas reachabilitysize incorrect\n"); + } + } + + 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 + 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); + 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/src/engine/botlib/be_aas_file.h b/src/engine/botlib/be_aas_file.h new file mode 100644 index 0000000000..a7c4b5fc43 --- /dev/null +++ b/src/engine/botlib/be_aas_file.h @@ -0,0 +1,56 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_file.h + * + * desc: AAS + * + * + *****************************************************************************/ + +#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/src/engine/botlib/be_aas_funcs.h b/src/engine/botlib/be_aas_funcs.h new file mode 100644 index 0000000000..19bc73a009 --- /dev/null +++ b/src/engine/botlib/be_aas_funcs.h @@ -0,0 +1,62 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_funcs.h + * + * desc: AAS + * + * + *****************************************************************************/ + +#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" + +// Ridah, route-tables +#include "be_aas_routetable.h" + +#endif //BSPCINCLUDE diff --git a/src/engine/botlib/be_aas_main.c b/src/engine/botlib/be_aas_main.c new file mode 100644 index 0000000000..8fb3582f73 --- /dev/null +++ b/src/engine/botlib/be_aas_main.c @@ -0,0 +1,555 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_main.c + * + * desc: AAS + * + * + *****************************************************************************/ + +#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 aasworlds[MAX_AAS_WORLDS]; + +aas_t *aasworld; + +//=========================================================================== +// +// 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, str); +} //end of the function AAS_Error + +// Ridah, multiple AAS worlds +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SetCurrentWorld(int index) +{ + if(index >= MAX_AAS_WORLDS || index < 0) + { + AAS_Error("AAS_SetCurrentWorld: index out of range\n"); + return; + } + + // set the current world pointer + aasworld = &aasworlds[index]; +} + +// done. + +//=========================================================================== +// +// 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); + return 0; // removed so the CS_ defines could be removed from be_aas_def.h +} //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); + return 0; // removed so the CS_ defines could be removed from be_aas_def.h +} //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 + + // Ridah, build/load the route-table + AAS_RT_BuildRouteTable(); + // done. + + AAS_InitTeamDeath(); + +} //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("nooptimize", "1"))) + { + AAS_Optimize(); + } + //save the AAS file + if(AAS_WriteAASFile((*aasworld).filename)) + { + botimport.Print(PRT_MESSAGE, "%s written succesfully\n", (*aasworld).filename); + } + 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) +{ + // Ridah, do each of the aasworlds + int i; + + for(i = 0; i < MAX_AAS_WORLDS; i++) + { + AAS_SetCurrentWorld(i); + + (*aasworld).time = time; + //invalidate the entities + + AAS_InvalidateEntities(); + //initialize AAS + AAS_ContinueInit(time); + //update team deaths + AAS_UpdateTeamDeath(); + // + (*aasworld).frameroutingupdates = 0; + // + /* Ridah, disabled for speed + 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 + (*aasworld).numframes++; + + return BLERR_NOERROR; +} //end of the function AAS_StartFrame + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_Time(void) +{ + return aasworld->time; +} + +//=========================================================================== +// basedir = Quake2 console basedir +// gamedir = Quake2 console gamedir +// mapname = name of the map without extension (.bsp) +// +// 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: - +//=========================================================================== + +// Ridah, modified this for multiple AAS files + +int AAS_LoadMap(const char *mapname) +{ + int errnum; + int i; + char this_mapname[256]; //, intstr[4]; + qboolean loaded = qfalse; + int missingErrNum = 0; + + for(i = 0; i < MAX_AAS_WORLDS; i++) + { + AAS_SetCurrentWorld(i); + + strncpy(this_mapname, mapname, 256); + //strncat( this_mapname, "_b", 256 ); + //sprintf( intstr, "%i", i ); + //strncat( this_mapname, intstr, 256 ); + + //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(this_mapname); + if(errnum != BLERR_NOERROR) + { + (*aasworld).loaded = qfalse; + // RF, we are allowed to skip one of the files, but not both + //return errnum; + missingErrNum = errnum; + continue; + } //end if + // + loaded = qtrue; + // + 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(); + } + + if(!loaded) + { + return missingErrNum; + } + + //everything went ok + return 0; +} //end of the function AAS_LoadMap + +// done. + +//=========================================================================== +// called when the library is first loaded +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Setup(void) +{ + // Ridah, just use the default world for entities + AAS_SetCurrentWorld(0); + + (*aasworlds).maxclients = (int)LibVarValue("maxclients", "128"); + (*aasworlds).maxentities = (int)LibVarValue("maxentities", "1024"); + //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) +{ + // Ridah, do each of the worlds + int i; + + for(i = 0; i < MAX_AAS_WORLDS; i++) + { + AAS_SetCurrentWorld(i); + + // Ridah, kill the route-table data + AAS_RT_ShutdownRouteTable(); + + 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(); + + if(i == 0) + { + //free the entities + if((*aasworld).entities) + { + FreeMemory((*aasworld).entities); + } + } + + //clear the (*aasworld) structure + 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/src/engine/botlib/be_aas_main.h b/src/engine/botlib/be_aas_main.h new file mode 100644 index 0000000000..9a087f0c28 --- /dev/null +++ b/src/engine/botlib/be_aas_main.h @@ -0,0 +1,85 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_main.h + * + * desc: AAS + * + * + *****************************************************************************/ + +#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); + +// Ridah +void AAS_SetCurrentWorld(int index); + +// done. diff --git a/src/engine/botlib/be_aas_move.c b/src/engine/botlib/be_aas_move.c new file mode 100644 index 0000000000..b375c63b6f --- /dev/null +++ b/src/engine/botlib/be_aas_move.c @@ -0,0 +1,1153 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_move.c + * + * desc: AAS + * + * + *****************************************************************************/ + +#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" + +//#define BSPC + +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 | CONTENTS_PLAYERCLIP) & ~CONTENTS_BODY); + 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.sv_friction = 6; + aassettings.sv_stopspeed = 100; + aassettings.sv_gravity = 800; + aassettings.sv_waterfriction = 1; + aassettings.sv_watergravity = 400; + aassettings.sv_maxvelocity = 320; + aassettings.sv_maxwalkvelocity = 300; + aassettings.sv_maxcrouchvelocity = 100; + aassettings.sv_maxswimvelocity = 150; + aassettings.sv_walkaccelerate = 10; + aassettings.sv_airaccelerate = 1; + aassettings.sv_swimaccelerate = 4; + aassettings.sv_maxstep = 18; + aassettings.sv_maxsteepness = 0.7; + aassettings.sv_maxwaterjump = 17; + // Ridah, calculate maxbarrier according to jumpvel and gravity + aassettings.sv_jumpvel = 270; + aassettings.sv_maxbarrier = 49; //-0.8 + (0.5 * aassettings.sv_gravity * (aassettings.sv_jumpvel / aassettings.sv_gravity) * (aassettings.sv_jumpvel / aassettings.sv_gravity)); + // done. + +} //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 ms_areanum) +{ + 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; + // RF, it does if they're in a monsterclip brush + if(!areanum) + { + areanum = ms_areanum; + } + //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) < 7) + { + // RF, if hanging on to the edge of a ladder, we have to account for bounding box touching + //if (AAS_PointInsideFace(abs(facenum), origin, 0.1)) return qtrue; + if(AAS_PointInsideFace(abs(facenum), origin, 2.0)) + { + 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; + bsp_trace_t trace; + vec3_t end, up = { 0, 0, 1 }; + //aas_plane_t *plane; + vec3_t mins, maxs; + + VectorCopy(origin, end); + end[2] -= 10; + + //trace = AAS_TraceClientBBox(origin, end, presencetype, passent); + AAS_PresenceTypeBoundingBox(presencetype, mins, maxs); + trace = AAS_Trace(origin, mins, maxs, end, passent, CONTENTS_SOLID | CONTENTS_PLAYERCLIP); + + //if in solid + if(trace.startsolid) + { + return qtrue; //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(trace.plane.normal, up) < aassettings.sv_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: - +//=========================================================================== +vec3_t VEC_UP = { 0, -1, 0 }; +vec3_t MOVEDIR_UP = { 0, 0, 1 }; +vec3_t VEC_DOWN = { 0, -2, 0 }; +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.1, + 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_ENTERLAVA | SE_HITGROUNDDAMAGE)) + { //----(SA) modified since slime is no longer deadly +// 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.sv_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 + +//=========================================================================== +// 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_PredictClientMovement(struct aas_clientmove_s *move, + int entnum, vec3_t origin, + int hitent, int onground, + vec3_t velocity, vec3_t cmdmove, + int cmdframes, int maxframes, float frametime, int stopevent, int stopareanum, int visualize) +{ + float sv_friction, sv_stopspeed, sv_gravity, sv_waterfriction; + float sv_watergravity; + float sv_walkaccelerate, sv_airaccelerate, sv_swimaccelerate; + float sv_maxwalkvelocity, sv_maxcrouchvelocity, sv_maxswimvelocity; + float sv_maxstep, sv_maxsteepness, sv_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], mins, maxs; + vec3_t org, end, feet, start, stepend, lastorg, wishdir; + vec3_t frame_test_vel, old_frame_test_vel, left_test_vel, savevel; + vec3_t up = { 0, 0, 1 }; + cplane_t *plane, *plane2, *lplane; + + //aas_trace_t trace, steptrace; + bsp_trace_t trace, steptrace; + + if(visualize) + { + +// These debugging tools are not currently available in bspc. Mad Doctor I, 1/27/2003. +#ifndef BSPC + AAS_ClearShownPolygons(); + AAS_ClearShownDebugLines(); +#endif + + } + + // don't let us succeed on interaction with area 0 + if(stopareanum == 0) + { + stopevent &= ~(SE_ENTERAREA | SE_HITGROUNDAREA); + } + + if(frametime <= 0) + { + frametime = 0.1; + } + // + sv_friction = aassettings.sv_friction; + sv_stopspeed = aassettings.sv_stopspeed; + sv_gravity = aassettings.sv_gravity; + sv_waterfriction = aassettings.sv_waterfriction; + sv_watergravity = aassettings.sv_watergravity; + sv_maxwalkvelocity = aassettings.sv_maxwalkvelocity; // * frametime; + sv_maxcrouchvelocity = aassettings.sv_maxcrouchvelocity; // * frametime; + sv_maxswimvelocity = aassettings.sv_maxswimvelocity; // * frametime; + sv_walkaccelerate = aassettings.sv_walkaccelerate; + sv_airaccelerate = aassettings.sv_airaccelerate; + sv_swimaccelerate = aassettings.sv_swimaccelerate; + sv_maxstep = aassettings.sv_maxstep; + sv_maxsteepness = aassettings.sv_maxsteepness; + sv_jumpvel = aassettings.sv_jumpvel * frametime; + // + memset(move, 0, sizeof(aas_clientmove_t)); + memset(&trace, 0, sizeof(bsp_trace_t)); + AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, mins, maxs); + //start at the current origin + VectorCopy(origin, org); + org[2] += 0.25; + // test this position, if it's in solid, move it up to adjust for capsules + //trace = AAS_TraceClientBBox(org, org, PRESENCE_NORMAL, entnum); + trace = AAS_Trace(org, mins, maxs, org, entnum, (CONTENTS_SOLID | CONTENTS_PLAYERCLIP) & ~CONTENTS_BODY); + while(trace.startsolid) + { + org[2] += 8; + //trace = AAS_TraceClientBBox(org, org, PRESENCE_NORMAL, entnum); + trace = AAS_Trace(org, mins, maxs, org, entnum, (CONTENTS_SOLID | CONTENTS_PLAYERCLIP) & ~CONTENTS_BODY); + if(trace.startsolid && (org[2] - origin[2] > 16)) + { + move->stopevent = SE_NONE; + return qfalse; + } + } + //velocity to test for the first frame + VectorScale(velocity, frametime, frame_test_vel); + // + jump_frame = -1; + lplane = NULL; + //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 ? sv_watergravity : sv_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 ? sv_friction : sv_waterfriction; + //apply friction + VectorScale(frame_test_vel, 1 / frametime, frame_test_vel); + AAS_ApplyFriction(frame_test_vel, friction, sv_stopspeed, frametime); + VectorScale(frame_test_vel, frametime, frame_test_vel); + } //end if + crouch = qfalse; + //apply command movement + if(cmdframes < 0) + { + // cmdmove is the destination, we should keep moving towards it + VectorSubtract(cmdmove, org, wishdir); + VectorNormalize(wishdir); + VectorScale(wishdir, sv_maxwalkvelocity, wishdir); + VectorCopy(frame_test_vel, savevel); + VectorScale(wishdir, frametime, frame_test_vel); + if(!swimming) + { + frame_test_vel[2] = savevel[2]; + } + } + else if(n < cmdframes) + { + ax = 0; + maxvel = sv_maxwalkvelocity; + accelerate = sv_airaccelerate; + VectorCopy(cmdmove, wishdir); + if(onground) + { + if(cmdmove[2] < -300) + { + crouch = qtrue; + maxvel = sv_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] = sv_jumpvel - (gravity * 0.1 * frametime) + 5; + jump_frame = n; + //jumping so air accelerate + accelerate = sv_airaccelerate; + } //end if + else + { + accelerate = sv_walkaccelerate; + } //end else + ax = 2; + } //end if + if(swimming) + { + maxvel = sv_maxswimvelocity; + accelerate = sv_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 > sv_maxacceleration) velchange = sv_maxacceleration; + else if (velchange < -sv_maxacceleration) velchange = -sv_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, PRESENCE_NORMAL, entnum); + trace = AAS_Trace(org, mins, maxs, end, entnum, (CONTENTS_SOLID | CONTENTS_PLAYERCLIP) & ~CONTENTS_BODY); + // +//#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_HITENT) + { + if(trace.fraction < 1.0 && trace.ent == hitent) + { + areanum = AAS_PointAreaNum(org); + VectorCopy(org, move->endpos); + VectorScale(frame_test_vel, 1 / frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_HITENT; + move->presencetype = (*aasworld).areasettings[areanum].presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } + } + + if(stopevent & SE_ENTERAREA) + { + numareas = AAS_TraceAreas(org, trace.endpos, areas, points, 20); + for(i = 0; i < numareas; i++) + { + if(areas[i] == stopareanum) + { + VectorCopy(points[i], move->endpos); + VectorScale(frame_test_vel, 1 / frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_ENTERAREA; + move->presencetype = (*aasworld).areasettings[areas[i]].presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end for + } //end if + + if(stopevent & SE_STUCK) + { + if(trace.fraction < 1.0) + { + plane = &trace.plane; + //if (Q_fabs(plane->normal[2]) <= sv_maxsteepness) { + VectorNormalize2(frame_test_vel, wishdir); + if(DotProduct(plane->normal, wishdir) < -0.8) + { + areanum = AAS_PointAreaNum(org); + VectorCopy(org, move->endpos); + VectorScale(frame_test_vel, 1 / frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_STUCK; + move->presencetype = (*aasworld).areasettings[areanum].presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } + } + } + + //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 = &trace.plane; + // + if(stopevent & SE_HITGROUNDAREA) + { + if(DotProduct(plane->normal, up) > sv_maxsteepness) + { + VectorCopy(org, start); + start[2] += 0.5; + if((stopareanum < 0 && AAS_PointAreaNum(start)) || (AAS_PointAreaNum(start) == stopareanum)) + { + VectorCopy(start, move->endpos); + VectorScale(frame_test_vel, 1 / frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_HITGROUNDAREA; + move->presencetype = (*aasworld).areasettings[stopareanum].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] += sv_maxstep; + //steptrace = AAS_TraceClientBBox(start, stepend, PRESENCE_NORMAL, entnum); + steptrace = + AAS_Trace(start, mins, maxs, stepend, entnum, (CONTENTS_SOLID | CONTENTS_PLAYERCLIP) & ~CONTENTS_BODY); + // + if(!steptrace.startsolid) + { + plane2 = &steptrace.plane; + if(DotProduct(plane2->normal, up) > sv_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); + // RF: from PM_SlideMove() + // if this is the same plane we hit before, nudge velocity + // out along it, which fixes some epsilon issues with + // non-axial planes + if(lplane && DotProduct(lplane->normal, plane->normal) > 0.99) + { + VectorAdd(plane->normal, left_test_vel, left_test_vel); + } + lplane = plane; + //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) > sv_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); + VectorCopy(frame_test_vel, move->velocity); + move->trace = trace; + move->stopevent = SE_HITGROUNDDAMAGE; + areanum = AAS_PointAreaNum(org); + if(areanum) + { + move->presencetype = (*aasworld).areasettings[areanum].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); + VectorScale(frame_test_vel, 1 / frametime, move->velocity); + move->stopevent = event & stopevent; + move->presencetype = (*aasworld).areasettings[areanum].presencetype; + move->endcontents = pc; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + // + onground = AAS_OnGround(org, PRESENCE_NORMAL, entnum); + //if onground and on the ground for at least one whole frame + if(onground) + { + if(stopevent & SE_HITGROUND) + { + VectorCopy(org, move->endpos); + VectorScale(frame_test_vel, 1 / frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_HITGROUND; + areanum = AAS_PointAreaNum(org); + if(areanum) + { + move->presencetype = (*aasworld).areasettings[areanum].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); + VectorScale(frame_test_vel, 1 / frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_LEAVEGROUND; + areanum = AAS_PointAreaNum(org); + if(areanum) + { + move->presencetype = (*aasworld).areasettings[areanum].presencetype; + } + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end else if + else if(stopevent & SE_GAP) + { + bsp_trace_t gaptrace; + + VectorCopy(org, start); + VectorCopy(start, end); + end[2] -= 48 + aassettings.sv_maxbarrier; + //gaptrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); + gaptrace = AAS_Trace(start, mins, maxs, end, -1, (CONTENTS_SOLID | CONTENTS_PLAYERCLIP) & ~CONTENTS_BODY); + //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.sv_maxstep - 1) + { + if(!(AAS_PointContents(end) & (CONTENTS_WATER | CONTENTS_SLIME))) + { //----(SA) modified since slime is no longer deadly +// if (!(AAS_PointContents(end) & CONTENTS_WATER)) + VectorCopy(lastorg, move->endpos); + VectorScale(frame_test_vel, 1 / frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_GAP; + areanum = AAS_PointAreaNum(org); + if(areanum) + { + move->presencetype = (*aasworld).areasettings[areanum].presencetype; + } + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + } //end if + } //end else if + if(stopevent & SE_TOUCHJUMPPAD) + { + if((*aasworld).areasettings[AAS_PointAreaNum(org)].contents & AREACONTENTS_JUMPPAD) + { + VectorCopy(org, move->endpos); + VectorScale(frame_test_vel, 1 / frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_TOUCHJUMPPAD; + areanum = AAS_PointAreaNum(org); + if(areanum) + { + move->presencetype = (*aasworld).areasettings[areanum].presencetype; + } + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + if(stopevent & SE_TOUCHTELEPORTER) + { + if((*aasworld).areasettings[AAS_PointAreaNum(org)].contents & AREACONTENTS_TELEPORTER) + { + VectorCopy(org, move->endpos); + VectorScale(frame_test_vel, 1 / frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_TOUCHTELEPORTER; + areanum = AAS_PointAreaNum(org); + if(areanum) + { + move->presencetype = (*aasworld).areasettings[areanum].presencetype; + } + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + } //end for + // + areanum = AAS_PointAreaNum(org); + VectorCopy(org, move->endpos); + VectorScale(frame_test_vel, 1 / frametime, move->velocity); + move->stopevent = SE_NONE; + move->presencetype = aasworld->areasettings ? aasworld->areasettings[areanum].presencetype : 0; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + // + return qtrue; +} //end of the function AAS_PredictClientMovement + +//=========================================================================== +// +// 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.1, 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 sv_gravity, sv_maxvelocity; + float maxjump, height2fall, t, top; + vec3_t dir; + + sv_gravity = aassettings.sv_gravity; + sv_maxvelocity = aassettings.sv_maxvelocity; + + //maximum height a player can jump with the given initial z velocity + maxjump = 0.5 * sv_gravity * (zvel / sv_gravity) * (zvel / sv_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 = sv_maxvelocity; + return 0; + } //end if + //time a player takes to fall the height + t = sqrt(height2fall / (0.5 * sv_gravity)); + //direction from start to end + VectorSubtract(end, start, dir); + //calculate horizontal speed + *velocity = sqrt(dir[0] * dir[0] + dir[1] * dir[1]) / (t + zvel / sv_gravity); + //the horizontal speed must be lower than the max speed + if(*velocity > sv_maxvelocity) + { + *velocity = sv_maxvelocity; + return 0; + } //end if + return 1; +} //end of the function AAS_HorizontalVelocityForJump diff --git a/src/engine/botlib/be_aas_move.h b/src/engine/botlib/be_aas_move.h new file mode 100644 index 0000000000..c79a44bdbe --- /dev/null +++ b/src/engine/botlib/be_aas_move.h @@ -0,0 +1,83 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_move.h + * + * desc: AAS + * + * + *****************************************************************************/ + +#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); +//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, int ms_areanum); + +//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/src/engine/botlib/be_aas_optimize.c b/src/engine/botlib/be_aas_optimize.c new file mode 100644 index 0000000000..44bb495637 --- /dev/null +++ b/src/engine/botlib/be_aas_optimize.c @@ -0,0 +1,766 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_optimize.c + * + * desc: decreases the .aas file size after the reachabilities have + * been calculated, just dumps all the faces, edges and vertexes + * + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_libvar.h" +//#include "l_utils.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 +{ + //vertixes + 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; + + /// + // RF, addition of removal of non-reachability 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; + //clusters + int numclusters; + aas_cluster_t *clusters; +*/// + int *vertexoptimizeindex; + int *edgeoptimizeindex; + int *faceoptimizeindex; + // + int *areakeep; + int *arearemap; + int *removedareas; + int *reachabilityremap; +} 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]; + 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]; + 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 == 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 == TRAVEL_JUMPPAD) + { + continue; + } + //NOTE: for TRAVEL_FUNCBOB the facenum and edgenum contain other coded information + if((*aasworld).reachability[i].traveltype == 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 + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveNonReachabilityAlloc(optimized_t * optimized) +{ + optimized->areas = (aas_area_t *) GetClearedMemory((*aasworld).numareas * sizeof(aas_area_t)); + // + optimized->areasettings = (aas_areasettings_t *) GetClearedMemory((*aasworld).numareas * sizeof(aas_areasettings_t)); + // + optimized->reachability = (aas_reachability_t *) GetClearedMemory((*aasworld).reachabilitysize * sizeof(aas_reachability_t)); + optimized->reachabilitysize = (*aasworld).reachabilitysize; +/* // + optimized->nodes = (aas_node_t *) GetClearedMemory((*aasworld).numnodes * sizeof(aas_node_t)); + optimized->numnodes = (*aasworld).numnodes; + // + optimized->portals = (aas_portals_t *) GetClearedMemory((*aasworld).numportals * sizeof(aas_portal_t)); + optimized->numportals = (*aasworld).numportals; + // + optimized->clusters = (aas_cluster_t *) GetClearedMemory((*aasworld).numclusters * sizeof(aas_cluster_t)); + optimized->numclusters = (*aasworld).numclusters; +*/// + optimized->areakeep = (int *)GetClearedMemory((*aasworld).numareas * sizeof(int)); + optimized->arearemap = (int *)GetClearedMemory((*aasworld).numareas * sizeof(int)); + optimized->removedareas = (int *)GetClearedMemory((*aasworld).numareas * sizeof(int)); + optimized->reachabilityremap = (int *)GetClearedMemory((*aasworld).reachabilitysize * sizeof(int)); +} //end of the function AAS_OptimizeAlloc + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_NumberClusterAreas(int clusternum); +void AAS_RemoveNonReachability(void) +{ + int i, j; + optimized_t optimized; + int removed = 0, valid = 0, numoriginalareas; + int validreach = 0; + aas_face_t *face; + + AAS_RemoveNonReachabilityAlloc(&optimized); + // mark portal areas as non-removable + if(aasworld->numportals) + { + for(i = 0; i < aasworld->numportals; i++) + { + optimized.areakeep[aasworld->portals[i].areanum] = qtrue; + } + } + // remove non-reachability areas + numoriginalareas = aasworld->numareas; + for(i = 1; i < (*aasworld).numareas; i++) + { + // is this a reachability area? + if(optimized.areakeep[i] || aasworld->areasettings[i].numreachableareas) + { + optimized.arearemap[i] = ++valid; + // copy it to the optimized areas + optimized.areas[valid] = (*aasworld).areas[i]; + optimized.areas[valid].areanum = valid; + continue; + } + // yes it is if it made it to here + removed++; + optimized.removedareas[i] = qtrue; + } + optimized.numareas = valid + 1; + // store the new areas + if((*aasworld).areas) + { + FreeMemory((*aasworld).areas); + } + (*aasworld).areas = optimized.areas; + (*aasworld).numareas = optimized.numareas; + // + // remove reachabilities that are no longer required + validreach = 1; + for(i = 1; i < aasworld->reachabilitysize; i++) + { + optimized.reachabilityremap[i] = validreach; + if(optimized.removedareas[aasworld->reachability[i].areanum]) + { + continue; + } + // save this reachability + optimized.reachability[validreach] = aasworld->reachability[i]; + optimized.reachability[validreach].areanum = optimized.arearemap[optimized.reachability[validreach].areanum]; + // + validreach++; + } + optimized.reachabilitysize = validreach; + // store the reachabilities + if((*aasworld).reachability) + { + FreeMemory((*aasworld).reachability); + } + (*aasworld).reachability = optimized.reachability; + (*aasworld).reachabilitysize = optimized.reachabilitysize; + // + // remove and update areasettings + for(i = 1; i < numoriginalareas; i++) + { + if(optimized.removedareas[i]) + { + continue; + } + j = optimized.arearemap[i]; + optimized.areasettings[j] = aasworld->areasettings[i]; + optimized.areasettings[j].firstreachablearea = optimized.reachabilityremap[aasworld->areasettings[i].firstreachablearea]; + optimized.areasettings[j].numreachableareas = + 1 + optimized.reachabilityremap[aasworld->areasettings[i].firstreachablearea + + aasworld->areasettings[i].numreachableareas - 1] - + optimized.areasettings[j].firstreachablearea; + } + // + // update faces (TODO: remove unused) + for(i = 1, face = &aasworld->faces[1]; i < aasworld->numfaces; i++, face++) + { + if(!optimized.removedareas[face->backarea]) + { + face->backarea = optimized.arearemap[face->backarea]; + } + else + { // now points to a void + face->backarea = 0; + } + if(!optimized.removedareas[face->frontarea]) + { + face->frontarea = optimized.arearemap[face->frontarea]; + } + else + { + face->frontarea = 0; + } + } + // store the areasettings + if((*aasworld).areasettings) + { + FreeMemory((*aasworld).areasettings); + } + (*aasworld).areasettings = optimized.areasettings; + (*aasworld).numareasettings = optimized.numareas; + // + // update nodes + for(i = 1; i < (*aasworld).numnodes; i++) + { + for(j = 0; j < 2; j++) + { + if(aasworld->nodes[i].children[j] < 0) + { + if(optimized.removedareas[-aasworld->nodes[i].children[j]]) + { + aasworld->nodes[i].children[j] = 0; //make it solid + } + else + { // remap + aasworld->nodes[i].children[j] = -optimized.arearemap[-aasworld->nodes[i].children[j]]; + } + } + } + } + // + // update portal areanums + for(i = 0; i < aasworld->numportals; i++) + { + aasworld->portals[i].areanum = optimized.arearemap[aasworld->portals[i].areanum]; + } + // update clusters and portals + for(i = 0; i < (*aasworld).numclusters; i++) + { + AAS_NumberClusterAreas(i); + } + // free temporary memory + FreeMemory(optimized.areakeep); + FreeMemory(optimized.arearemap); + FreeMemory(optimized.removedareas); + FreeMemory(optimized.reachabilityremap); + //print some nice stuff :) + botimport.Print(PRT_MESSAGE, "%i non-reachability areas removed, %i remain.\n", removed, valid); +} //end of the function AAS_Optimize + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveNonGrounded(void) +{ + int i, j; + optimized_t optimized; + int removed = 0, valid = 0, numoriginalareas; + int validreach = 0; + aas_face_t *face; + + AAS_RemoveNonReachabilityAlloc(&optimized); + // mark portal areas as non-removable + if(aasworld->numportals) + { + for(i = 0; i < aasworld->numportals; i++) + { + optimized.areakeep[aasworld->portals[i].areanum] = qtrue; + } + } + // remove non-reachability areas + numoriginalareas = aasworld->numareas; + for(i = 1; i < (*aasworld).numareas; i++) + { + // is this a grounded area? + if(optimized.areakeep[i] || (aasworld->areasettings[i].areaflags & (AREA_GROUNDED | AREA_LADDER))) + { + optimized.arearemap[i] = ++valid; + // copy it to the optimized areas + optimized.areas[valid] = (*aasworld).areas[i]; + optimized.areas[valid].areanum = valid; + continue; + } + // yes it is if it made it to here + removed++; + optimized.removedareas[i] = qtrue; + } + optimized.numareas = valid + 1; + // store the new areas + if((*aasworld).areas) + { + FreeMemory((*aasworld).areas); + } + (*aasworld).areas = optimized.areas; + (*aasworld).numareas = optimized.numareas; + // + // remove reachabilities that are no longer required + validreach = 1; + for(i = 1; i < aasworld->reachabilitysize; i++) + { + optimized.reachabilityremap[i] = validreach; + if(optimized.removedareas[aasworld->reachability[i].areanum]) + { + continue; + } + // save this reachability + optimized.reachability[validreach] = aasworld->reachability[i]; + optimized.reachability[validreach].areanum = optimized.arearemap[optimized.reachability[validreach].areanum]; + // + validreach++; + } + optimized.reachabilitysize = validreach; + // store the reachabilities + if((*aasworld).reachability) + { + FreeMemory((*aasworld).reachability); + } + (*aasworld).reachability = optimized.reachability; + (*aasworld).reachabilitysize = optimized.reachabilitysize; + // + // remove and update areasettings + for(i = 1; i < numoriginalareas; i++) + { + if(optimized.removedareas[i]) + { + continue; + } + j = optimized.arearemap[i]; + optimized.areasettings[j] = aasworld->areasettings[i]; + optimized.areasettings[j].firstreachablearea = optimized.reachabilityremap[aasworld->areasettings[i].firstreachablearea]; + optimized.areasettings[j].numreachableareas = + 1 + optimized.reachabilityremap[aasworld->areasettings[i].firstreachablearea + + aasworld->areasettings[i].numreachableareas - 1] - + optimized.areasettings[j].firstreachablearea; + } + // + // update faces (TODO: remove unused) + for(i = 1, face = &aasworld->faces[1]; i < aasworld->numfaces; i++, face++) + { + if(!optimized.removedareas[face->backarea]) + { + face->backarea = optimized.arearemap[face->backarea]; + } + else + { // now points to a void + face->backarea = 0; + } + if(!optimized.removedareas[face->frontarea]) + { + face->frontarea = optimized.arearemap[face->frontarea]; + } + else + { + face->frontarea = 0; + } + } + // store the areasettings + if((*aasworld).areasettings) + { + FreeMemory((*aasworld).areasettings); + } + (*aasworld).areasettings = optimized.areasettings; + (*aasworld).numareasettings = optimized.numareas; + // + // update nodes + for(i = 1; i < (*aasworld).numnodes; i++) + { + for(j = 0; j < 2; j++) + { + if(aasworld->nodes[i].children[j] < 0) + { + if(optimized.removedareas[-aasworld->nodes[i].children[j]]) + { + aasworld->nodes[i].children[j] = 0; //make it solid + } + else + { // remap + aasworld->nodes[i].children[j] = -optimized.arearemap[-aasworld->nodes[i].children[j]]; + } + } + } + } + // + // update portal areanums + for(i = 0; i < aasworld->numportals; i++) + { + aasworld->portals[i].areanum = optimized.arearemap[aasworld->portals[i].areanum]; + } + // update clusters and portals + for(i = 0; i < (*aasworld).numclusters; i++) + { + AAS_NumberClusterAreas(i); + } + // free temporary memory + FreeMemory(optimized.areakeep); + FreeMemory(optimized.arearemap); + FreeMemory(optimized.removedareas); + FreeMemory(optimized.reachabilityremap); + //print some nice stuff :) + botimport.Print(PRT_MESSAGE, "%i non-grounded areas removed, %i remain.\n", removed, valid); +} //end of the function AAS_Optimize diff --git a/src/engine/botlib/be_aas_optimize.h b/src/engine/botlib/be_aas_optimize.h new file mode 100644 index 0000000000..f6c752d7f8 --- /dev/null +++ b/src/engine/botlib/be_aas_optimize.h @@ -0,0 +1,46 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_optimize.h + * + * desc: AAS + * + * + *****************************************************************************/ + +void AAS_Optimize(void); +void AAS_RemoveNonReachability(void); +void AAS_RemoveNonGrounded(void); diff --git a/src/engine/botlib/be_aas_reach.c b/src/engine/botlib/be_aas_reach.c new file mode 100644 index 0000000000..35bc943190 --- /dev/null +++ b/src/engine/botlib/be_aas_reach.c @@ -0,0 +1,5216 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + + +/***************************************************************************** + * name: be_aas_reach.c + * + * desc: reachability calculations + * + * + *****************************************************************************/ + +#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); + +//#include "../../../gladiator/bspc/aas_store.h" + +extern botlib_import_t botimport; + +//#define REACHDEBUG + +//NOTE: all travel times are in hundreth of a second +//maximum fall delta before getting damaged + +// Ridah, tweaked for Wolf AI +#define FALLDELTA_5DAMAGE 25 //40 +#define FALLDELTA_10DAMAGE 40 //60 +// done. + +//maximum number of reachability links +#define AAS_MAX_REACHABILITYSIZE 128000 +//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 + +// Ridah, tweaked this, routing issues around small areas +#define INSIDEUNITS_WALKEND 5 // original +//#define INSIDEUNITS_WALKEND 0.2 // new + +// Ridah, added this for better walking off ledges +#define INSIDEUNITS_WALKOFFLEDGEEND 15 + +#define INSIDEUNITS_WALKSTART 0.1 +#define INSIDEUNITS_WATERJUMP 15 +//travel times in hundreth of a second + +// Ridah, tweaked these for Wolf AI +#define REACH_MIN_TIME 4 // always at least this much time for a reachability +#define WATERJUMP_TIME 700 //7 seconds +#define TELEPORT_TIME 50 //0.5 seconds +#define BARRIERJUMP_TIME 900 //fixed value? +#define STARTCROUCH_TIME 300 //3 sec to start crouching +#define STARTGRAPPLE_TIME 500 //using the grapple costs a lot of time +#define STARTWALKOFFLEDGE_TIME 300 //3 seconds +#define STARTJUMP_TIME 500 //3 seconds for jumping + +#define FALLDAMAGE_5_TIME 400 //extra travel time when falling hurts +#define FALLDAMAGE_10_TIME 900 //extra travel time when falling hurts +// done. + +//maximum height the bot may fall down when jumping +#define MAX_JUMPFALLHEIGHT 450 +//area flag used for weapon jumping +#define AREA_WEAPONJUMP 8192 //valid area to weapon jump to +#define AREA_JUMPSRC 16384 //valid area to JUMP FROM +//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 = qfalse; + +//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; + +typedef struct +{ + int destarea; + vec3_t srcpos; + vec3_t destpos; +} aas_jumplink_t; + +static aas_jumplink_t *jumplinks; + +//=========================================================================== +// +// 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: cannot enable next line right now because the reachability + // does not have to be 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_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(AAS_AreaGrounded(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 (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"); + */ + 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_AASLinkEntity(absmins, absmaxs, -1); + //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) +{ + 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 + // RF, if this area is disabled, then fail + if((*aasworld).areasettings[areanum].areaflags & AREA_DISABLED) + { + return 0; + } + return (*aasworld).areasettings[areanum].numreachableareas; +} //end of the function AAS_AreaReachability + +//=========================================================================== +// 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; + 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]; + plane = &(*aasworld).planes[face->planenum]; + 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 + +//=========================================================================== +// 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.sv_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.sv_gravity; + t = sqrt(Q_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 sv_jumpvel) +{ + float sv_gravity; + + sv_gravity = aassettings.sv_gravity; + //maximum height a player can jump with the given initial z velocity + return 0.5 * sv_gravity * (sv_jumpvel / sv_gravity) * (sv_jumpvel / sv_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 sv_jumpvel) +{ + float sv_gravity, sv_maxvelocity, t; + + sv_gravity = aassettings.sv_gravity; + sv_maxvelocity = aassettings.sv_maxvelocity; + //time a player takes to fall the height + t = sqrt(MAX_JUMPFALLHEIGHT / (0.5 * sv_gravity)); + //maximum distance + return sv_maxvelocity * (t + sv_jumpvel / sv_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; + } +}*/ +//=========================================================================== +// 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; + } +}*/ +//=========================================================================== +// 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_AreaDoNotEnter(int areanum) +{ + return ((*aasworld).areasettings[areanum].contents & AREACONTENTS_DONOTENTER); +} //end of the function AAS_AreaDoNotEnter + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaDoNotEnterLarge(int areanum) +{ + return ((*aasworld).areasettings[areanum].contents & AREACONTENTS_DONOTENTER_LARGE); +} //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.sv_jumpvel / (aassettings.sv_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; + 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 += STARTCROUCH_TIME; + } //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; + 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, ground_bestend, ground_bestnormal; + vec3_t water_beststart, water_bestend, water_bestnormal; + 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.sv_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 += STARTCROUCH_TIME; + } //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.sv_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.sv_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 = WATERJUMP_TIME; + 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.sv_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 = BARRIERJUMP_TIME; //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.sv_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); + + // Ridah +// VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); + VectorMA(ground_bestend, INSIDEUNITS_WALKOFFLEDGEEND, 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 + //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) + { + //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 = STARTWALKOFFLEDGE_TIME + Q_fabs(ground_bestdist) * 50 / aassettings.sv_gravity; + //if falling from too high and not falling into water + if(!AAS_AreaSwim(area2num) && !AAS_AreaJumpPad(area2num)) + { + if(AAS_FallDelta(ground_bestdist) > FALLDELTA_5DAMAGE) + { + lreach->traveltime += FALLDAMAGE_5_TIME; + } //end if + if(AAS_FallDelta(ground_bestdist) > FALLDELTA_10DAMAGE) + { + lreach->traveltime += FALLDAMAGE_10_TIME; + } //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 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: - +//=========================================================================== +/* Ridah, moved to q_math.c +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; + float sv_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 }; + 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]; + // + // RF, check for a forced jump reachability + if(aasworld->areasettings[area1num].areaflags & AREA_JUMPSRC) + { + if(jumplinks[area1num].destarea == area2num) + { + //create a new reachability link + lreach = AAS_AllocReachability(); + if(!lreach) + { + return qfalse; + } + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = 0; + VectorCopy(jumplinks[area1num].srcpos, lreach->start); + VectorCopy(jumplinks[area1num].destpos, lreach->end); + lreach->traveltype = TRAVEL_JUMP; + + VectorCopy(jumplinks[area1num].srcpos, beststart); + VectorCopy(jumplinks[area1num].destpos, bestend); + VectorSubtract(bestend, beststart, dir); + height = dir[2]; + dir[2] = 0; + lreach->traveltime = STARTJUMP_TIME + VectorDistance(beststart, bestend) * 240 / aassettings.sv_maxwalkvelocity; + // + if(AAS_FallDelta(beststart[2] - bestend[2]) > FALLDELTA_5DAMAGE) + { + lreach->traveltime += FALLDAMAGE_5_TIME; + } //end if + else if(AAS_FallDelta(beststart[2] - bestend[2]) > FALLDELTA_10DAMAGE) + { + lreach->traveltime += FALLDAMAGE_10_TIME; + } //end if + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + reach_jump++; + return qtrue; + } + } + // + sv_jumpvel = aassettings.sv_jumpvel; + //maximum distance a player can jump + maxjumpdistance = 2 * AAS_MaxJumpDistance(sv_jumpvel); + //maximum height a player can jump with the given initial z velocity + maxjumpheight = AAS_MaxJumpHeight(sv_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 the fall would damage the bot + // + if(AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) + { + //FIXME: why multiply with 1.2??? + speed *= 1.2; + traveltype = TRAVEL_WALKOFFLEDGE; + } //end if + else if(bestdist <= 48 && Q_fabs(beststart[2] - bestend[2]) < 8) + { + speed = 400; + 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(sv_jumpvel, beststart, bestend, &speed)) + { + return qfalse; + } + 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(DotProduct(plane->normal, up) >= 0.7) + { + if(!(AAS_PointContents(trace.endpos) & CONTENTS_LAVA)) + { //----(SA) modified since slime is no longer deadly +// if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME))) + if(teststart[2] - trace.endpos[2] <= aassettings.sv_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(DotProduct(plane->normal, up) >= 0.7) + { + if(!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA /*|CONTENTS_SLIME */ ))) + { + if(teststart[2] - trace.endpos[2] <= aassettings.sv_maxbarrier) + { + return qfalse; + } + } //end if + } //end if + } //end if + // + VectorSubtract(bestend, beststart, dir); + dir[2] = 0; + VectorNormalize(dir); + // + VectorScale(dir, speed, velocity); + //get command movement + VectorClear(cmdmove); + if(traveltype == TRAVEL_JUMP) + { + cmdmove[2] = aassettings.sv_jumpvel; + } + else + { + cmdmove[2] = 0; + } + // + AAS_PredictClientMovement(&move, -1, beststart, PRESENCE_NORMAL, qtrue, + velocity, cmdmove, 3, 30, 0.1, + SE_HITGROUND | SE_ENTERWATER | SE_ENTERSLIME | SE_ENTERLAVA | SE_HITGROUNDDAMAGE, 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_ENTERLAVA) + { + return qfalse; //----(SA) modified since slime is no longer deadly + } +// if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA)) 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 + for(i = 0; i <= 32; i += 8) + { + VectorMA(move.endpos, -i, dir, teststart); + teststart[2] += 0.125; + if(AAS_PointAreaNum(teststart) == area2num) + { + break; + } + } //end for + if(i > 32) + { + return qfalse; + } + // +#ifdef REACHDEBUG + //create the reachability + Log_Write("jump reachability between %d and %d\r\n", area1num, area2num); +#endif //REACHDEBUG + //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 == TRAVEL_WALKOFFLEDGE && height > VectorLength(dir)) + { + lreach->traveltime = STARTWALKOFFLEDGE_TIME + height * 50 / aassettings.sv_gravity; + } + else + { + lreach->traveltime = STARTJUMP_TIME + VectorDistance(bestend, beststart) * 240 / aassettings.sv_maxwalkvelocity; + } //end if + // + if(!AAS_AreaJumpPad(area2num)) + { + if(AAS_FallDelta(beststart[2] - bestend[2]) > FALLDELTA_5DAMAGE) + { + lreach->traveltime += FALLDAMAGE_5_TIME; + } //end if + else if(AAS_FallDelta(beststart[2] - bestend[2]) > FALLDELTA_10DAMAGE) + { + lreach->traveltime += FALLDAMAGE_10_TIME; + } //end if + } //end if + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + if(traveltype == TRAVEL_JUMP) + { + reach_jump++; + } + else + { + reach_walkoffledge++; + } + // + return qtrue; + } //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, lowestedgenum; + int face1num, face2num, ladderface1num, ladderface2num; + int ladderface1vertical, ladderface2vertical, firstv; + float face1area, face2area, bestface1area, bestface2area; + float sv_jumpvel, maxjumpheight; + vec3_t area1point, area2point, v1, v2, up = { 0, 0, 1 }; + vec3_t mid, lowestpoint, start, end, sharededgevec, dir; + aas_area_t *area1, *area2; + aas_face_t *face1, *face2, *ladderface1, *ladderface2; + 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; + } + // + sv_jumpvel = aassettings.sv_jumpvel; + //maximum height a player can jump with the given initial z velocity + maxjumpheight = AAS_MaxJumpHeight(sv_jumpvel); + + area1 = &(*aasworld).areas[area1num]; + area2 = &(*aasworld).areas[area2num]; + // + ladderface1 = NULL; + ladderface2 = NULL; + ladderface1num = 0; //make compiler happy + ladderface2num = 0; //make compiler happy + bestface1area = -9999; + bestface2area = -9999; + sharededgenum = 0; //make compiler happy + lowestedgenum = 0; //make compiler happy + // + 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 = Q_fabs(DotProduct(plane1->normal, up)) < 0.1; + ladderface2vertical = Q_fabs(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 + && Q_fabs(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, plane2->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 + // RF, this should be a ladder reachability, since we usually climb down ladders in Wolf (falling is possibly dangerous) + 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); +#if 1 // testing ladder descend instead of walk off ledge + lreach->end[2] -= 16; + VectorMA(lreach->start, -15, plane1->normal, lreach->start); + lreach->traveltype = TRAVEL_LADDER; + lreach->traveltime = 100; // have to turn around +#else + lreach->traveltype = TRAVEL_WALKOFFLEDGE; + lreach->traveltime = 10; +#endif + 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 REACHDEBUG + if(trace.startsolid) + { + Log_Write("trace from area %d started in solid\r\n", area1num); + } //end if +#endif //REACHDEBUG + // + 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(Q_fabs(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 REACHDEBUG + Log_Write("jump up to ladder reach between %d and %d\r\n", area2num, area1num); +#endif //REACHDEBUG + } //end if +#ifdef REACHDEBUG + else + { + Log_Write("jump too high between area %d and %d\r\n", area2num, area1num); + } +#endif //REACHDEBUG + } //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 + +//=========================================================================== +// 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; + vec3_t origin, destorigin, mins, maxs, end, angles = { 0, 0, 0 }; + vec3_t mid; + aas_lreachability_t *lreach; + 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 REACHDEBUG + botimport.Print(PRT_MESSAGE, "trigger_multiple model = \"%s\"\n", model); +//#endif REACHDEBUG + 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 REACHDEBUG + botimport.Print(PRT_MESSAGE, "trigger_teleport model = \"%s\"\n", model); +//#endif REACHDEBUG + 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)) + { + 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); + } //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->traveltime = TELEPORT_TIME; + 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: - +//=========================================================================== +#define REACHDEBUG +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 REACHDEBUG + Log_Write("AAS_Reachability_Elevator\r\n"); +#endif //REACHDEBUG + 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 REACHDEBUG + Log_Write("found func plat\r\n"); +#endif //REACHDEBUG + 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; + // + botimport.Print(PRT_MESSAGE, "pos2[2] = %1.1f pos1[2] = %1.1f\n", pos2[2], pos1[2]); + //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->traveltime = height * 100 / speed; + if(!lreach->traveltime) + { + lreach->traveltime = 50; + } + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + //don't go any further to the outside + n = 9999; + // +#ifdef REACHDEBUG + Log_Write("elevator reach from %d to %d\r\n", area1num, area2num); +#endif //REACHDEBUG + // + 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.sv_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.1)) + { + //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 + // + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL); + // + VectorAdd(mins, maxs, mid); + VectorScale(mid, 0.5, mid); + //VectorAdd(mid, origin, 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 + /* + 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 + 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->traveltime = 300; + 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, modelnum, area2num, visualize; + float speed, zvel, hordist, dist, time, height, gravity, forward; + aas_face_t *face2; + aas_area_t *area2; + aas_lreachability_t *lreach; + vec3_t areastart, facecenter, dir, cmdmove, teststart; + vec3_t velocity, origin, ent2origin, angles, absmins, absmaxs; + aas_clientmove_t move; + aas_trace_t trace; + int ent, ent2; + aas_link_t *areas, *link; + char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY]; + char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; + + 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; + } + // + 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 REACHDEBUG + 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 // REACHDEBUG + 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 == 5772) + { + 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_multiple 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; + 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.1, SE_HITGROUND | SE_ENTERWATER | SE_ENTERSLIME | SE_ENTERLAVA | SE_HITGROUNDDAMAGE | SE_TOUCHJUMPPAD | SE_TOUCHTELEPORTER, 0, qfalse); //qtrue); + area2num = AAS_PointAreaNum(move.endpos); + 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->traveltime = 200; + lreach->next = areareachability[link->areanum]; + areareachability[link->areanum] = lreach; + // + reach_jumppad++; + } //end for + } //end if + } //end if + // + if(Q_fabs(velocity[0]) > 100 || Q_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 (the jump is not possible) then there's no jump reachability created + 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.1, + 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))) + { + 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 = area2num; + //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->traveltime = 250; + 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_LAVA) + { //----(SA) modified since slime is no longer deadly +// 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 = STARTGRAPPLE_TIME + 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, dent; + vec3_t mins = { -18, -18, -24 }, maxs = + { + 18, 18, 40}; + vec3_t origin, destorigin; + int areanum, weaponjumpareas, spawnflags, destareanum; + char classname[MAX_EPAIRKEY]; + char target[MAX_EPAIRKEY]; + char targetname[MAX_EPAIRKEY]; + + // + weaponjumpareas = 0; + jumplinks = (aas_jumplink_t *) GetClearedMemory(aasworld->numareas * sizeof(aas_jumplink_t)); + // + for(ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if(!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) + { + continue; + } + if(!strcmp(classname, "bot_jump_source")) + { + if(!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY)) + { + continue; + } + // find the destination + for(dent = AAS_NextBSPEntity(0); dent; dent = AAS_NextBSPEntity(dent)) + { + if(!AAS_ValueForBSPEpairKey(dent, "targetname", targetname, MAX_EPAIRKEY)) + { + continue; + } + if(!strcmp(target, targetname)) + { + // match found + break; + } + } + // if it failed, ignore and print message + if(!dent) + { + botimport.Print(PRT_MESSAGE, "WARNING: %s doesn't have a matching bot_jump_dest (target = %s)\n", + classname, target); + continue; + } + // success + // find source area + 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); + if(!areanum) + { + botimport.Print(PRT_MESSAGE, "%s in void area at (%1.1f %1.1f %1.1f)\n", + classname, origin[0], origin[1], origin[2]); + continue; + } + } + else + { + botimport.Print(PRT_MESSAGE, "%s has no origin (%s)\n", classname, target); + continue; + } + // find dest area + if(!AAS_ValueForBSPEpairKey(dent, "classname", classname, MAX_EPAIRKEY)) + { + continue; + } + if(AAS_VectorForBSPEpairKey(dent, "origin", destorigin)) + { + spawnflags = 0; + AAS_IntForBSPEpairKey(dent, "spawnflags", &spawnflags); + //if not a stationary item + if(!(spawnflags & 1)) + { + if(!AAS_DropToFloor(destorigin, mins, maxs)) + { + botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n", + classname, destorigin[0], destorigin[1], destorigin[2]); + } //end if + } //end if + //areanum = AAS_PointAreaNum(origin); + destareanum = AAS_BestReachableArea(destorigin, mins, maxs, destorigin); + if(!destareanum) + { + botimport.Print(PRT_MESSAGE, "%s in void area at (%1.1f %1.1f %1.1f)\n", + classname, destorigin[0], destorigin[1], destorigin[2]); + continue; + } + } + else + { + botimport.Print(PRT_MESSAGE, "%s has no origin (%s)\n", classname, targetname); + continue; + } + + //the bot may jump between these areas + (*aasworld).areasettings[areanum].areaflags |= AREA_JUMPSRC; + jumplinks[areanum].destarea = destareanum; + VectorCopy(origin, jumplinks[areanum].srcpos); + VectorCopy(destorigin, jumplinks[areanum].destpos); + // + if(!AAS_AreaGrounded(areanum)) + { + botimport.Print(PRT_MESSAGE, "area not grounded\n"); + } + // + weaponjumpareas++; + } //end if +/* + 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_sp5") || + !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; + 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; + + 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 < 270) + { + //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.1, + SE_ENTERWATER | SE_ENTERSLIME | + SE_ENTERLAVA | SE_HITGROUNDDAMAGE | + SE_TOUCHJUMPPAD | SE_HITGROUNDAREA, area2num, qfalse); + //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; + } + else + { + lreach->traveltype = TRAVEL_ROCKETJUMP; + } + lreach->traveltime = 300; + 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; + 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_LAVA) + { //----(SA) modified since slime is no longer deadly +// 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 + 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 = + STARTWALKOFFLEDGE_TIME + Q_fabs(mid[2] - trace.endpos[2]) * 50 / aassettings.sv_gravity; + if(!AAS_AreaSwim(reachareanum) && !AAS_AreaJumpPad(reachareanum)) + { + if(AAS_FallDelta(mid[2] - trace.endpos[2]) > FALLDELTA_5DAMAGE) + { + lreach->traveltime += FALLDAMAGE_5_TIME; + } //end if + else if(AAS_FallDelta(mid[2] - trace.endpos[2]) > FALLDELTA_10DAMAGE) + { + lreach->traveltime += FALLDAMAGE_10_TIME; + } //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; + // RF, enforce the min reach time + if(reach->traveltime < REACH_MIN_TIME) + { + reach->traveltime = REACH_MIN_TIME; + } + // + 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).reachabilityareas >= (*aasworld).numareas + 2) + { + return qfalse; + } + //if starting with area 1 (area 0 is a dummy) + if((*aasworld).reachabilityareas == 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).reachabilityareas + (int)framereachability; + start_time = Sys_MilliSeconds(); + //loop over the areas + for(i = (*aasworld).reachabilityareas; i < (*aasworld).numareas && i < todo; i++) + { + (*aasworld).reachabilityareas++; + //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 +// Ridah, no grapple +// AAS_Reachability_Grapple(i, j); + //check for a weapon jump reachability +// Ridah, no weapon jumping +// 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).reachabilityareas * 1000 / (*aasworld).numareas > lastpercentage) + { + break; + } + } //end for + // + if((*aasworld).reachabilityareas == (*aasworld).numareas) + { + botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float)100.0); + botimport.Print(PRT_MESSAGE, "\nplease wait while storing reachability...\n"); + (*aasworld).reachabilityareas++; + } //end if + //if this is the last step in the reachability calculations + else if((*aasworld).reachabilityareas == (*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).reachabilityareas++; + // + botimport.Print(PRT_MESSAGE, "calculating clusters...\n"); + } //end if + else + { + lastpercentage = (*aasworld).reachabilityareas * 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).reachabilityareas = (*aasworld).numareas + 2; + return; + } //end if +#else + (*aasworld).reachabilityareas = (*aasworld).numareas + 2; + return; +#endif //BSPC + } //end if + (*aasworld).savefile = qtrue; + //start with area 1 because area zero is a dummy + (*aasworld).reachabilityareas = 1; + //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/src/engine/botlib/be_aas_reach.h b/src/engine/botlib/be_aas_reach.h new file mode 100644 index 0000000000..62a5f68ca5 --- /dev/null +++ b/src/engine/botlib/be_aas_reach.h @@ -0,0 +1,98 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_reach.h + * + * desc: AAS + * + * + *****************************************************************************/ + +#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 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); +#define AAS_AreaCrouch( areanum ) ( ( !( aasworld->areasettings[areanum].presencetype & PRESENCE_NORMAL ) ) ? qtrue : qfalse ) + +//returns true if a player can swim in this area +//int AAS_AreaSwim(int areanum); +#define AAS_AreaSwim( areanum ) ( ( aasworld->areasettings[areanum].areaflags & AREA_LIQUID ) ? qtrue : qfalse ) + +//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); + +//returns true if the area is donotenterlarge +int AAS_AreaDoNotEnterLarge(int areanum); diff --git a/src/engine/botlib/be_aas_route.c b/src/engine/botlib/be_aas_route.c new file mode 100644 index 0000000000..5783b1fc0f --- /dev/null +++ b/src/engine/botlib/be_aas_route.c @@ -0,0 +1,4270 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_route.c + * + * desc: AAS + * + * + *****************************************************************************/ + +#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.3 //crouch speed = 100 +#define DISTANCEFACTOR_SWIM 1 //should be 0.66, swim speed = 150 +#define DISTANCEFACTOR_WALK 0.33 //walk speed = 300 + +// Ridah, scale traveltimes with ground steepness of area +#define GROUNDSTEEPNESS_TIMESCALE 1 // this is the maximum scale, 1 being the usual for a flat ground + +//cache refresh time +#define CACHE_REFRESHTIME 15.0 //15 seconds refresh time + +#define DEFAULT_MAX_ROUTINGCACHESIZE "16384" + +extern aas_t aasworlds[MAX_AAS_WORLDS]; + + +/* + + 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; +int max_frameroutingupdates; + +// Ridah, routing memory calls go here, so we can change between Hunk/Zone easily +void *AAS_RoutingGetMemory(int size) +{ + return GetClearedMemory(size); +} + +void AAS_RoutingFreeMemory(void *ptr) +{ + FreeMemory(ptr); +} + +// done. + +//=========================================================================== +// +// 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: - +//=========================================================================== +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: - +//=========================================================================== +int AAS_TravelFlagForType(int traveltype) +{ + if(traveltype < 0 || traveltype >= MAX_TRAVELTYPES) + { + return TFL_INVALID; + } + return aasworld->travelflagfortype[traveltype]; +} //end of the function AAS_TravelFlagForType + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_RoutingTime(void) +{ + return AAS_Time(); +} //end of the function AAS_RoutingTime + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeRoutingCache(aas_routingcache_t * cache) +{ + routingcachesize -= cache->size; + AAS_RoutingFreeMemory(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 + if(aasworld->portalcache) + { + 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_TeamTravelFlagsForAreaFlags(int areaflags) +{ + int travelflags = 0; + + // + if(areaflags & AREA_TEAM_FLAGS) + { + if(areaflags & AREA_TEAM_AXIS) + { + travelflags |= TFL_TEAM_AXIS; + } + if(areaflags & AREA_TEAM_ALLIES) + { + travelflags |= TFL_TEAM_ALLIES; + } + if(areaflags & AREA_TEAM_AXIS_DISGUISED) + { + travelflags |= TFL_TEAM_AXIS_DISGUISED; + } + if(areaflags & AREA_TEAM_ALLIES_DISGUISED) + { + travelflags |= TFL_TEAM_AXIS_DISGUISED; + } + if(areaflags & AREA_AVOID_AXIS) + { + travelflags |= TFL_TEAM_AXIS; + } + if(areaflags & AREA_AVOID_ALLIES) + { + travelflags |= TFL_TEAM_ALLIES; + } + } + // + return travelflags; +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ClearClusterTeamFlags(int areanum) +{ + int clusternum; + + // + clusternum = aasworld->areasettings[areanum].cluster; + if(clusternum > 0) + { + aasworld->clusterTeamTravelFlags[clusternum] = -1; // recalculate + } +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CalculateClusterTeamFlags(int clusternum) +{ + int i; + + // + if(clusternum < 0) + { + return; + } + // + aasworld->clusterTeamTravelFlags[clusternum] = 0; + for(i = 1; i < aasworld->numareas; i++) + { + if(aasworld->areasettings[i].cluster == clusternum) + { + aasworld->clusterTeamTravelFlags[clusternum] |= AAS_TeamTravelFlagsForAreaFlags(aasworld->areasettings[i].areaflags); + } + } +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_EnableRoutingArea(int areanum, int enable) +{ + int flags; + int bitflag; // flag to set or clear + + 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 + + if((enable & 1) || (enable < 0)) + { + // clear all flags + bitflag = + AREA_AVOID | AREA_DISABLED | AREA_TEAM_AXIS | AREA_TEAM_ALLIES | AREA_TEAM_AXIS_DISGUISED | + AREA_TEAM_ALLIES_DISGUISED; + } + else if(enable & 0x10) + { + bitflag = AREA_AVOID; + } + else if(enable & 0x20) + { + bitflag = AREA_TEAM_AXIS; + } + else if(enable & 0x40) + { + bitflag = AREA_TEAM_ALLIES; + } + else if(enable & 0x80) + { + bitflag = AREA_TEAM_AXIS_DISGUISED; + } + else if(enable & 0x100) + { + bitflag = AREA_TEAM_ALLIES_DISGUISED; + } + else + { + bitflag = AREA_DISABLED; + } + + // remove avoidance flag + enable &= 1; + + flags = aasworld->areasettings[areanum].areaflags & bitflag; + if(enable < 0) + { + return !flags; + } + + if(enable) + { + aasworld->areasettings[areanum].areaflags &= ~bitflag; + } + else + { + aasworld->areasettings[areanum].areaflags |= bitflag; + } + + // if the status of the area changed + if((flags & bitflag) != (aasworld->areasettings[areanum].areaflags & bitflag)) + { + //remove all routing cache involving this area + AAS_RemoveRoutingCacheUsingArea(areanum); + // recalculate the team flags that are used in this cluster + AAS_ClearClusterTeamFlags(areanum); + } //end if + return !flags; +} //end of the function AAS_EnableRoutingArea + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_EnableAllAreas(void) +{ + int i; + + for(i = 0; i < (*aasworld).numareas; i++) + { + if((*aasworld).areasettings[i].areaflags & AREA_DISABLED) + { + AAS_EnableRoutingArea(i, qtrue); + } + } +} + +//=========================================================================== +// +// 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) + { + AAS_RoutingFreeMemory(aasworld->reversedreachability); + } + //allocate memory for the reversed reachability links + ptr = + (char *)AAS_RoutingGetMemory(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 other areas for reachability links to the area + for(i = 1; i < aasworld->numareas; i++) + { + //settings of the area + settings = &(aasworld->areasettings[i]); + //check the reachability links + for(n = 0; n < settings->numreachableareas; n++) + { + // Gordon: Temp hack for b0rked last area in + if(settings->firstreachablearea < 0 || settings->firstreachablearea >= (*aasworld).reachabilitysize) + { + Com_Printf("^1WARNING: settings->firstreachablearea out of range\n"); + continue; + } + + //reachability link + reach = &aasworld->reachability[settings->firstreachablearea + n]; + + if((reach->areanum < 0 || (reach->areanum >= aasworld->reachabilitysize))) + { + Com_Printf("^1WARNING: reach->areanum out of range\n"); + continue; + } + + 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 //DEBUG +} //end of the function AAS_CreateReversedReachability + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +// Gordon: always returns 1, so er?... +float AAS_AreaGroundSteepnessScale(int areanum) +{ + return (1.0 + aasworld->areasettings[areanum].groundsteepness * (float)(GROUNDSTEEPNESS_TIMESCALE - 1)); +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end) +{ + int intdist; + float dist; + + // Ridah, factor in the groundsteepness now + dist = VectorDistance(start, end); // * AAS_AreaGroundSteepnessScale(areanum); // Gordon: useless as it returns 1 all the time... + + if(AAS_AreaCrouch(areanum)) + { + dist *= DISTANCEFACTOR_CROUCH; //if crouch only area +/* } else if( AAS_AreaSwim(areanum)) { // Gordon: again, uselss as it's a multiply by 1 + dist *= DISTANCEFACTOR_SWIM; //if swim area */ + } + else + { + dist *= DISTANCEFACTOR_WALK; //normal walk area + } + + intdist = myftol(dist); + + //make sure the distance isn't zero + if(intdist <= 0) + { + return 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) + { + AAS_RoutingFreeMemory(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 * revreach->numlinks * sizeof(unsigned short); + } //end for + //allocate memory for the area travel times + ptr = (char *)AAS_RoutingGetMemory(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]; + // + if(settings->numreachableareas) + { + aasworld->areatraveltimes[i] = (unsigned short **)ptr; + ptr += settings->numreachableareas * sizeof(unsigned short *); + // + reach = &aasworld->reachability[settings->firstreachablearea]; + for(l = 0; l < settings->numreachableareas; l++, reach++) + { + aasworld->areatraveltimes[i][l] = (unsigned short *)ptr; + ptr += revreach->numlinks * sizeof(unsigned short); + //reachability link + // + 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 //DEBUG +} //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) + { + AAS_RoutingFreeMemory(aasworld->portalmaxtraveltimes); + } + + aasworld->portalmaxtraveltimes = (int *)AAS_RoutingGetMemory(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: - +//=========================================================================== +void AAS_UnlinkCache(aas_routingcache_t *cache) +{ + if (cache->time_next) cache->time_next->time_prev = cache->time_prev; + else newestcache = cache->time_prev; + if (cache->time_prev) cache->time_prev->time_next = cache->time_next; + else 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 (newestcache) + { + newestcache->time_next = cache; + cache->time_prev = cache; + } //end if + else + { + oldestcache = cache; + cache->time_prev = NULL; + } //end else + cache->time_next = NULL; + newestcache = cache; +} //end of the function AAS_LinkCache*/ +//=========================================================================== +// +// 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: - +//=========================================================================== +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 *) AAS_RoutingGetMemory(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 + AAS_RoutingFreeMemory(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 *)AAS_RoutingGetMemory(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 + AAS_RoutingFreeMemory(aasworld->portalcache); + aasworld->portalcache = NULL; +} //end of the function AAS_FreeAllPortalCache + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitPortalCache(void) +{ + // + aasworld->portalcache = (aas_routingcache_t **) AAS_RoutingGetMemory(aasworld->numareas * sizeof(aas_routingcache_t *)); +} //end of the function AAS_InitPortalCache + +// +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeAreaVisibility(void) +{ + int i; + + if(aasworld->areavisibility) + { + for(i = 0; i < aasworld->numareas; i++) + { + if(aasworld->areavisibility[i]) + { + FreeMemory(aasworld->areavisibility[i]); + } + } + } + if(aasworld->areavisibility) + { + FreeMemory(aasworld->areavisibility); + } + aasworld->areavisibility = NULL; + if(aasworld->decompressedvis) + { + FreeMemory(aasworld->decompressedvis); + } + aasworld->decompressedvis = NULL; +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitRoutingUpdate(void) +{ +// int i, maxreachabilityareas; + + //free routing update fields if already existing + if(aasworld->areaupdate) + { + AAS_RoutingFreeMemory(aasworld->areaupdate); + } + + aasworld->areaupdate = (aas_routingupdate_t *) AAS_RoutingGetMemory(aasworld->numareas * sizeof(aas_routingupdate_t)); + + if(aasworld->portalupdate) + { + AAS_RoutingFreeMemory(aasworld->portalupdate); + } + //allocate memory for the portal update fields + aasworld->portalupdate = + (aas_routingupdate_t *) AAS_RoutingGetMemory((aasworld->numportals + 1) * sizeof(aas_routingupdate_t)); +} //end of the function AAS_InitRoutingUpdate + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +void AAS_CreateAllRoutingCache(void) +{ + int i, j, k, t, tfl, numroutingareas; + aas_areasettings_t *areasettings; + aas_reachability_t *reach; + + numroutingareas = 0; + tfl = TFL_DEFAULT & ~(TFL_JUMPPAD | TFL_ROCKETJUMP | TFL_BFGJUMP | TFL_GRAPPLEHOOK | TFL_DOUBLEJUMP | TFL_RAMPJUMP | TFL_STRAFEJUMP | TFL_LAVA); //----(SA) modified since slime is no longer deadly +// tfl = TFL_DEFAULT & ~(TFL_JUMPPAD|TFL_ROCKETJUMP|TFL_BFGJUMP|TFL_GRAPPLEHOOK|TFL_DOUBLEJUMP|TFL_RAMPJUMP|TFL_STRAFEJUMP|TFL_SLIME|TFL_LAVA); + botimport.Print(PRT_MESSAGE, "AAS_CreateAllRoutingCache\n"); + // + for(i = 1; i < aasworld->numareas; i++) + { + if(!AAS_AreaReachability(i)) + { + continue; + } + areasettings = &aasworld->areasettings[i]; + for(k = 0; k < areasettings->numreachableareas; k++) + { + reach = &aasworld->reachability[areasettings->firstreachablearea + k]; + if(aasworld->travelflagfortype[reach->traveltype] & tfl) + { + break; + } + } + if(k >= areasettings->numreachableareas) + { + continue; + } + aasworld->areasettings[i].areaflags |= AREA_USEFORROUTING; + numroutingareas++; + } + for(i = 1; i < aasworld->numareas; i++) + { + if(!(aasworld->areasettings[i].areaflags & AREA_USEFORROUTING)) + { + continue; + } + for(j = 1; j < aasworld->numareas; j++) + { + if(i == j) + { + continue; + } + if(!(aasworld->areasettings[j].areaflags & AREA_USEFORROUTING)) + { + continue; + } + t = AAS_AreaTravelTimeToGoalArea(j, aasworld->areawaypoints[j], i, tfl); + aasworld->frameroutingupdates = 0; + //if (t) break; + //Log_Write("traveltime from %d to %d is %d", i, j, t); + } //end for + } //end for +} //end of the function AAS_CreateAllRoutingCache + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +unsigned short CRC_ProcessString(unsigned char *data, int length); + +//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 reachcrc; + int numportalcache; + int numareacache; +} routecacheheader_t; + +#define RCID ( ( 'C' << 24 ) + ( 'R' << 16 ) + ( 'E' << 8 ) + 'M' ) +#define RCVERSION 16 + +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, size; + aas_routingcache_t *cache; + aas_cluster_t *cluster; + fileHandle_t fp; + char filename[MAX_QPATH]; + routecacheheader_t routecacheheader; + byte *buf; + + buf = (byte *) GetClearedMemory(aasworld->numareas * 2 * sizeof(byte)); // in case it ends up bigger than the decompressedvis, which is rare but possible + + 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.reachcrc = + CRC_ProcessString((unsigned char *)aasworld->reachability, sizeof(aas_reachability_t) * aasworld->reachabilitysize); + routecacheheader.numportalcache = numportalcache; + routecacheheader.numareacache = numareacache; + //write the header + botimport.FS_Write(&routecacheheader, sizeof(routecacheheader_t), fp); + //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); + } //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); + } //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, buf); + botimport.FS_Write(&size, sizeof(int), fp); + botimport.FS_Write(buf, size, fp); + } + // write the waypoints + botimport.FS_Write(aasworld->areawaypoints, sizeof(vec3_t) * aasworld->numareas, fp); + // + botimport.FS_FCloseFile(fp); + botimport.Print(PRT_MESSAGE, "\nroute cache written to %s\n", filename); +} //end of the function AAS_WriteRouteCache + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_routingcache_t *AAS_ReadCache(fileHandle_t fp) +{ + int size, i; + aas_routingcache_t *cache; + + botimport.FS_Read(&size, sizeof(size), fp); + size = LittleLong(size) + 0; // silence the warning + cache = (aas_routingcache_t *) AAS_RoutingGetMemory(size); + cache->size = size; + botimport.FS_Read((unsigned char *)cache + sizeof(size), size - sizeof(size), fp); + + if(1 != LittleLong(1)) + { + cache->time = LittleFloat(cache->time); + cache->cluster = LittleLong(cache->cluster); + cache->areanum = LittleLong(cache->areanum); + cache->origin[0] = LittleFloat(cache->origin[0]); + cache->origin[1] = LittleFloat(cache->origin[1]); + cache->origin[2] = LittleFloat(cache->origin[2]); + cache->starttraveltime = LittleFloat(cache->starttraveltime); + cache->travelflags = LittleLong(cache->travelflags); + } + +// cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) - sizeof(unsigned short) + +// (size - sizeof(aas_routingcache_t) + sizeof(unsigned short)) / 3 * 2; + cache->reachabilities = (unsigned char *)cache + sizeof(aas_routingcache_t) + ((size - sizeof(aas_routingcache_t)) / 3) * 2; + + //DAJ BUGFIX for missing byteswaps for traveltimes + size = (size - sizeof(aas_routingcache_t)) / 3 + 1; + for(i = 0; i < size; i++) + { + cache->traveltimes[i] = LittleShort(cache->traveltimes[i]); + } + return cache; +} //end of the function AAS_ReadCache + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_ReadRouteCache(void) +{ + int i, clusterareanum, size; + fileHandle_t fp = 0; + 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) + { + botimport.FS_FCloseFile(fp); + AAS_Error("%s is not a route cache dump\n"); + return qfalse; + } //end if + if(routecacheheader.version != RCVERSION) + { + botimport.FS_FCloseFile(fp); + AAS_Error("route cache dump has wrong version %d, should be %d", routecacheheader.version, RCVERSION); + return qfalse; + } //end if + if(routecacheheader.numareas != aasworld->numareas) + { + botimport.FS_FCloseFile(fp); + //AAS_Error("route cache dump has wrong number of areas\n"); + return qfalse; + } //end if + if(routecacheheader.numclusters != aasworld->numclusters) + { + botimport.FS_FCloseFile(fp); + //AAS_Error("route cache dump has wrong number of clusters\n"); + return qfalse; + } //end if +#if defined( MACOSX ) + // the crc table stuff is endian orientated.... +#else + if(routecacheheader.areacrc != CRC_ProcessString((unsigned char *)aasworld->areas, sizeof(aas_area_t) * aasworld->numareas)) + { + botimport.FS_FCloseFile(fp); + //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)) + { + botimport.FS_FCloseFile(fp); + //AAS_Error("route cache dump cluster CRC incorrect\n"); + return qfalse; + } //end if + if(routecacheheader.reachcrc != + CRC_ProcessString((unsigned char *)aasworld->reachability, sizeof(aas_reachability_t) * aasworld->reachabilitysize)) + { + botimport.FS_FCloseFile(fp); + //AAS_Error("route cache dump reachability CRC incorrect\n"); + return qfalse; + } //end if +#endif + //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); + } + } + // read the area waypoints + aasworld->areawaypoints = (vec3_t *) GetClearedMemory(aasworld->numareas * sizeof(vec3_t)); + botimport.FS_Read(aasworld->areawaypoints, aasworld->numareas * sizeof(vec3_t), fp); + // + botimport.FS_FCloseFile(fp); + return qtrue; +} //end of the function AAS_ReadRouteCache + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CreateVisibility(qboolean waypointsOnly); +void AAS_InitRouting(void) +{ + AAS_InitTravelFlagFromType(); + //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(); + // +#ifdef ROUTING_DEBUG + numareacacheupdates = 0; + numportalcacheupdates = 0; +#endif //ROUTING_DEBUG + // + routingcachesize = 0; + max_routingcachesize = 1024 * (int)LibVarValue("max_routingcache", DEFAULT_MAX_ROUTINGCACHESIZE); + max_frameroutingupdates = (int)LibVarGetValue("bot_frameroutingupdates"); + // + // enable this for quick testing of maps without enemies + if(LibVarGetValue("bot_norcd")) + { + // RF, create the waypoints for each area + AAS_CreateVisibility(qtrue); + } + else + { + // Ridah, load or create the routing cache + if(!AAS_ReadRouteCache()) + { + aasworld->initialized = qtrue; // Hack, so routing can compute traveltimes + AAS_CreateVisibility(qfalse); + // RF, removed, going back to dynamic routes + //AAS_CreateAllRoutingCache(); + aasworld->initialized = qfalse; + + AAS_WriteRouteCache(); // save it so we don't have to create it again + } + // done. + } +} //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 all the existing area visibility data + AAS_FreeAreaVisibility(); + // free cached travel times within areas + if(aasworld->areatraveltimes) + { + AAS_RoutingFreeMemory(aasworld->areatraveltimes); + } + aasworld->areatraveltimes = NULL; + // free cached maximum travel time through cluster portals + if(aasworld->portalmaxtraveltimes) + { + AAS_RoutingFreeMemory(aasworld->portalmaxtraveltimes); + } + aasworld->portalmaxtraveltimes = NULL; + // free reversed reachability links + if(aasworld->reversedreachability) + { + AAS_RoutingFreeMemory(aasworld->reversedreachability); + } + aasworld->reversedreachability = NULL; + // free routing algorithm memory + if(aasworld->areaupdate) + { + AAS_RoutingFreeMemory(aasworld->areaupdate); + } + aasworld->areaupdate = NULL; + if(aasworld->portalupdate) + { + AAS_RoutingFreeMemory(aasworld->portalupdate); + } + aasworld->portalupdate = NULL; + // free area waypoints + if(aasworld->areawaypoints) + { + FreeMemory(aasworld->areawaypoints); + } + aasworld->areawaypoints = NULL; +} //end of the function AAS_FreeRoutingCaches + +//=========================================================================== +// this function could be replaced by a bubble sort or for even faster +// routing by a B+ tree +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +static ID_INLINE void AAS_AddUpdateToList(aas_routingupdate_t ** updateliststart, + aas_routingupdate_t ** updatelistend, aas_routingupdate_t * update) +{ + if(!update->inlist) + { + if(*updatelistend) + { + (*updatelistend)->next = update; + } + else + { + *updateliststart = update; + } + update->prev = *updatelistend; + update->next = NULL; + *updatelistend = update; + update->inlist = qtrue; + } //end if +} //end of the function AAS_AddUpdateToList + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaContentsTravelFlag(int areanum) +{ + int contents, tfl; + + contents = aasworld->areasettings[areanum].contents; + tfl = 0; + if(contents & AREACONTENTS_WATER) + { + return tfl |= TFL_WATER; + } + else if(contents & AREACONTENTS_SLIME) + { + return tfl |= TFL_SLIME; + } + else if(contents & AREACONTENTS_LAVA) + { + return tfl |= TFL_LAVA; + } + else + { + tfl |= TFL_AIR; + } + if(contents & AREACONTENTS_DONOTENTER_LARGE) + { + tfl |= TFL_DONOTENTER_LARGE; + } + if(contents & AREACONTENTS_DONOTENTER) + { + return tfl |= TFL_DONOTENTER; + } + return tfl; +} //end of the function AAS_AreaContentsTravelFlag + +//=========================================================================== +// 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]; + 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; + // + //clear the routing update fields +// memset(aasworld->areaupdate, 0, aasworld->numareas * sizeof(aas_routingupdate_t)); + // + badtravelflags = ~areacache->travelflags; + // + clusterareanum = AAS_ClusterAreaNum(areacache->cluster, areacache->areanum); + if(clusterareanum >= numreachabilityareas) + { + return; + } + // + memset(startareatraveltimes, 0, sizeof(startareatraveltimes)); + // + curupdate = &aasworld->areaupdate[clusterareanum]; + curupdate->areanum = areacache->areanum; + //VectorCopy(areacache->origin, curupdate->start); + curupdate->areatraveltimes = aasworld->areatraveltimes[areacache->areanum][0]; + 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, flip the lists + 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(aasworld->travelflagfortype[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_AreaContentsTravelFlag(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 trying to avoid this area + if(aasworld->areasettings[reach->areanum].areaflags & AREA_AVOID) + { + t += 1000; + } + else if((aasworld->areasettings[reach->areanum].areaflags & AREA_AVOID_AXIS) && + (areacache->travelflags & TFL_TEAM_AXIS)) + { + t += 200; // + (curupdate->areatraveltimes[i] + reach->traveltime) * 30; + } + else if((aasworld->areasettings[reach->areanum].areaflags & AREA_AVOID_ALLIES) && + (areacache->travelflags & TFL_TEAM_ALLIES)) + { + t += 200; // + (curupdate->areatraveltimes[i] + reach->traveltime) * 30; + } + // + aasworld->frameroutingupdates++; + // + if(aasworld->areatraveltimes[nextareanum] && + (!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) + { + 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, qboolean forceUpdate) +{ + 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]; + + // RF, remove team-specific flags which don't exist in this cluster + travelflags &= ~TFL_TEAM_FLAGS | aasworld->clusterTeamTravelFlags[clusternum]; + + //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) + { + //NOTE: the number of routing updates is limited per frame + if(!forceUpdate && (aasworld->frameroutingupdates > max_frameroutingupdates)) + { + return NULL; + } //end if + 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 + //the cache has been accessed + cache->time = AAS_RoutingTime(); + 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 +// 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; + //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, flip the lists + 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, qtrue); + //take all portals of the cluster + for(i = 0; i < cluster->numportals; i++) + { + portalnum = aasworld->portalindex[cluster->firstportal + i]; + portal = &aasworld->portals[portalnum]; + // + 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; + portalcache->reachabilities[portalnum] = cache->reachabilities[clusterareanum]; + 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 actual portal area for the next update + nextupdate->tmptraveltime = t + aasworld->portalmaxtraveltimes[portalnum]; + if(!nextupdate->inlist) + { + 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 + //the cache has been accessed + cache->time = AAS_RoutingTime(); + 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; + aas_portalindex_t *pPortalnum; + + if(!aasworld->initialized) + { + return qfalse; + } + + if(areanum == goalareanum) + { + *traveltime = 1; + *reachnum = 0; + return qtrue; + } //end if + // + 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(routingcachesize > max_routingcachesize) + { + if(!AAS_FreeOldestCache()) + { + break; + } + } + // + if(AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goalareanum)) + { + travelflags |= TFL_DONOTENTER; + } //end if + if(AAS_AreaDoNotEnterLarge(areanum) || AAS_AreaDoNotEnterLarge(goalareanum)) + { + travelflags |= TFL_DONOTENTER_LARGE; + } //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; + } + } + else if(clusternum > 0 && goalclusternum < 0) + { //check if the goalarea is a portal of the area cluster + portal = &aasworld->portals[-goalclusternum]; + if(portal->frontcluster == clusternum || portal->backcluster == clusternum) + { + goalclusternum = clusternum; + } + } + + //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, qfalse); + // RF, note that the routing cache might be NULL now since we are restricting + // the updates per frame, hopefully rejected cache's will be requested again + // when things have settled down + if(!areacache) + { + return qfalse; + } + //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 qfalse; + } + //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; + } + // + besttime = 0; + bestreachnum = -1; + //the cluster the area is in + cluster = &aasworld->clusters[clusternum]; + //current area inside the current cluster + clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); + //if the area is NOT a reachability area + if(clusterareanum >= cluster->numreachabilityareas) + { + return qfalse; + } + // + pPortalnum = aasworld->portalindex + cluster->firstportal; + //find the portal of the area cluster leading towards the goal area + for(i = 0; i < cluster->numportals; i++, pPortalnum++) + { + portalnum = *pPortalnum; + //if the goal area isn't reachable from the portal + if(!portalcache->traveltimes[portalnum]) + { + continue; + } + // + portal = aasworld->portals + portalnum; + // if the area in disabled + if(aasworld->areasettings[portal->areanum].areaflags & AREA_DISABLED) + { + continue; + } + // if there is no reachability out of the area + if(!aasworld->areasettings[portal->areanum].numreachableareas) + { + continue; + } + //get the cache of the portal area + areacache = AAS_GetAreaRoutingCache(clusternum, portal->areanum, travelflags, qfalse); + // RF, this may be NULL if we were unable to calculate the cache this frame + if(!areacache) + { + return qfalse; + } + //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 area portal + // because we can't directly calculate the exact travel time + // to be more specific we don't know which reachability is used to travel + // into the portal area when coming from the current area + t += aasworld->portalmaxtraveltimes[portalnum]; + // + // Ridah, needs to be up here + *reachnum = aasworld->areasettings[areanum].firstreachablearea + areacache->reachabilities[clusterareanum]; + +//botimport.Print(PRT_MESSAGE, "portal reachability: %i\n", (int)areacache->reachabilities[clusterareanum] ); + + if(origin) + { + 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 + // Ridah, check a route was found + 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_AreaTravelTimeToGoalAreaCheckLoop(int areanum, vec3_t origin, int goalareanum, int travelflags, int loopareanum) +{ + int traveltime, reachnum; + aas_reachability_t *reach; + + if(AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum)) + { + reach = &aasworld->reachability[reachnum]; + if(loopareanum && reach->areanum == loopareanum) + { + return 0; // going here will cause a looped route + } + 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 + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach) +{ + if(!aasworld->initialized) + { + memset(reach, 0, sizeof(aas_reachability_t)); + return; + } //end if + if(num < 0 || num >= aasworld->reachabilitysize) + { + memset(reach, 0, sizeof(aas_reachability_t)); + return; + } //end if + 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 == TRAVEL_ELEVATOR) + { + if(aasworld->reachability[i].facenum == modelnum) + { + return i; + } + } //end if + else if(aasworld->reachability[i].traveltype == 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 && 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 + +//=========================================================================== +// run-length compression on zeros +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_CompressVis(byte * vis, int numareas, byte * dest) +{ + int j; + int rep; + + //int visrow; + byte *dest_p; + byte check; + + // + dest_p = dest; + //visrow = (numareas + 7)>>3; + + for(j = 0; j < numareas /*visrow */ ; j++) + { + *dest_p++ = vis[j]; + check = vis[j]; + //if (vis[j]) + // continue; + + rep = 1; + for(j++; j < numareas /*visrow */ ; j++) + if(vis[j] != check || rep == 255) + { + break; + } + else + { + rep++; + } + *dest_p++ = rep; + j--; + } + return dest_p - dest; +} //end of the function AAS_CompressVis + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DecompressVis(byte * in, int numareas, byte * decompressed) +{ + byte c; + byte *out; + + //int row; + byte *end; + + // initialize the vis data, only set those that are visible + memset(decompressed, 0, numareas); + + //row = (numareas+7)>>3; + out = decompressed; + end = (byte *) ((intptr_t)decompressed + numareas); + + do + { + /* + if (*in) + { + *out++ = *in++; + continue; + } + */ + + c = in[1]; + if(!c) + { + AAS_Error("DecompressVis: 0 repeat"); + } + if(*in) + { // we need to set these bits + memset(out, 1, c); + } + in += 2; + /* + while (c) + { + *out++ = 0; + c--; + } + */ + out += c; + } while(out < end); +} //end of the function AAS_DecompressVis + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaVisible(int srcarea, int destarea) +{ + if(!aasworld->areavisibility) + { +// botimport.Print(PRT_MESSAGE, "AAS_AreaVisible: no vis data available, returning qtrue\n" ); + return qtrue; + } + if(srcarea != aasworld->decompressedvisarea) + { + if(!aasworld->areavisibility[srcarea]) + { + return qfalse; + } + AAS_DecompressVis(aasworld->areavisibility[srcarea], aasworld->numareas, aasworld->decompressedvis); + aasworld->decompressedvisarea = srcarea; + } + return aasworld->decompressedvis[destarea]; +} //end of the function AAS_AreaVisible + +//=========================================================================== +// just center to center visibility checking... +// FIXME: implement a correct full vis +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CreateVisibility(qboolean waypointsOnly) +{ + int i, j, size, totalsize; + vec3_t endpos, mins, maxs; + bsp_trace_t trace; + byte *buf; + byte *validareas = NULL; + int numvalid = 0; + byte *areaTable = NULL; + int numAreas, numAreaBits; + + numAreas = aasworld->numareas; + numAreaBits = ((numAreas + 8) >> 3); + + if(!waypointsOnly) + { + validareas = (byte *) GetClearedMemory(numAreas * sizeof(byte)); + } + + aasworld->areawaypoints = (vec3_t *) GetClearedMemory(numAreas * sizeof(vec3_t)); + totalsize = numAreas * sizeof(byte *); + + for(i = 1; i < numAreas; i++) + { + if(!AAS_AreaReachability(i)) + { + continue; + } + + // find the waypoint + VectorCopy(aasworld->areas[i].center, endpos); + endpos[2] -= 256; + AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, mins, maxs); + trace = + AAS_Trace(aasworld->areas[i].center, mins, maxs, endpos, -1, + CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP); + if(trace.startsolid && trace.ent < ENTITYNUM_WORLD) + { + trace = + AAS_Trace(aasworld->areas[i].center, mins, maxs, endpos, trace.ent, + CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP); + } + if(!trace.startsolid && trace.fraction < 1 && AAS_PointAreaNum(trace.endpos) == i) + { + VectorCopy(trace.endpos, aasworld->areawaypoints[i]); + + if(!waypointsOnly) + { + validareas[i] = 1; + } + + numvalid++; + } + else + { + VectorClear(aasworld->areawaypoints[i]); + } + } + + if(waypointsOnly) + { + return; + } + + aasworld->areavisibility = (byte **) GetClearedMemory(numAreas * sizeof(byte *)); + + aasworld->decompressedvis = (byte *) GetClearedMemory(numAreas * sizeof(byte)); + + areaTable = (byte *) GetClearedMemory(numAreas * numAreaBits * sizeof(byte)); + + buf = (byte *) GetClearedMemory(numAreas * 2 * sizeof(byte)); // in case it ends up bigger than the decompressedvis, which is rare but possible + + for(i = 1; i < numAreas; i++) + { + if(!validareas[i]) + { + continue; + } + + for(j = 1; j < numAreas; j++) + { + aasworld->decompressedvis[j] = 0; + if(i == j) + { + aasworld->decompressedvis[j] = 1; + if(areaTable) + { + areaTable[(i * numAreaBits) + (j >> 3)] |= (1 << (j & 7)); + } + continue; + } + + if(!validareas[j]) + { + continue; + } + + // if we have already checked this combination, copy the result + if(areaTable && (i > j)) + { + // use the reverse result stored in the table + if(areaTable[(j * numAreaBits) + (i >> 3)] & (1 << (i & 7))) + { + aasworld->decompressedvis[j] = 1; + } + // done, move to the next area + continue; + } + + // RF, check PVS first, since it's much faster + if(!AAS_inPVS(aasworld->areawaypoints[i], aasworld->areawaypoints[j])) + { + continue; + } + + trace = AAS_Trace(aasworld->areawaypoints[i], NULL, NULL, aasworld->areawaypoints[j], -1, CONTENTS_SOLID); + if(trace.startsolid && trace.ent < ENTITYNUM_WORLD) + { + trace = + AAS_Trace(aasworld->areas[i].center, mins, maxs, endpos, trace.ent, + CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP); + } + + if(trace.fraction >= 1) + { + if(areaTable) + { + areaTable[(i * numAreaBits) + (j >> 3)] |= (1 << (j & 7)); + } + aasworld->decompressedvis[j] = 1; + } + } + + size = AAS_CompressVis(aasworld->decompressedvis, numAreas, buf); + aasworld->areavisibility[i] = (byte *) GetMemory(size); + memcpy(aasworld->areavisibility[i], buf, size); + totalsize += size; + } + + if(areaTable) + { + FreeMemory(areaTable); + } + + botimport.Print(PRT_MESSAGE, "AAS_CreateVisibility: compressed vis size = %i\n", totalsize); +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float VectorDistance(vec3_t v1, vec3_t v2); +extern void ProjectPointOntoVector(vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj); +int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, vec3_t enemyorigin, int enemyareanum, + int travelflags, float maxdist, vec3_t distpos) +{ + int i, j, nextareanum, badtravelflags, numreach, bestarea; + unsigned short int t, besttraveltime, enemytraveltime; + aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; + aas_reachability_t *reach; + float dist1, dist2; + float enemytraveldist; + vec3_t enemyVec; + qboolean startVisible; + vec3_t v1, v2, p; + +#define MAX_HIDEAREA_LOOPS 3000 + static float lastTime; + static int loopCount; + + // + if(!aasworld->areavisibility) + { + return 0; + } + // + if(srcnum < 0) + { // hack to force run this call + srcnum = -srcnum - 1; + lastTime = 0; + } + // don't run this more than once per frame + if(lastTime == AAS_Time() && loopCount >= MAX_HIDEAREA_LOOPS) + { + return 0; + } + if(lastTime != AAS_Time()) + { + loopCount = 0; + } + lastTime = AAS_Time(); + // + if(!aasworld->hidetraveltimes) + { + aasworld->hidetraveltimes = (unsigned short int *)GetClearedMemory(aasworld->numareas * sizeof(unsigned short int)); + } + else + { + memset(aasworld->hidetraveltimes, 0, aasworld->numareas * sizeof(unsigned short int)); + } //end else + // + if(!aasworld->visCache) + { + aasworld->visCache = (byte *) GetClearedMemory(aasworld->numareas * sizeof(byte)); + } + else + { + memset(aasworld->visCache, 0, aasworld->numareas * sizeof(byte)); + } //end else + besttraveltime = 0; + bestarea = 0; + if(enemyareanum) + { + enemytraveltime = AAS_AreaTravelTimeToGoalArea(areanum, origin, enemyareanum, travelflags); + } + VectorSubtract(enemyorigin, origin, enemyVec); + enemytraveldist = VectorNormalize(enemyVec); + startVisible = botimport.BotVisibleFromPos(enemyorigin, enemynum, origin, srcnum, qfalse); + // + badtravelflags = ~travelflags; + // + curupdate = &aasworld->areaupdate[areanum]; + curupdate->areanum = areanum; + VectorCopy(origin, curupdate->start); + // GORDON: TEMP: FIXME: just avoiding a crash for the moment + if(areanum == 0) + { + return 0; + } + 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 current list, flip the lists + 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(aasworld->travelflagfortype[reach->traveltype] & badtravelflags) + { + continue; + } + // + if(AAS_AreaContentsTravelFlag(reach->areanum) & badtravelflags) + { + continue; + } + // dont pass through ladder areas + if(aasworld->areasettings[reach->areanum].areaflags & AREA_LADDER) + { + continue; + } + // + if(aasworld->areasettings[reach->areanum].areaflags & AREA_DISABLED) + { + 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; + } + // if this moves us outside maxdist + if(distpos && (VectorDistance(reach->end, distpos) > maxdist)) + { + 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; + // inc the loopCount, we are starting to use a bit of cpu time + loopCount++; + // if this isn't the fastest route to this area, ignore + if(aasworld->hidetraveltimes[nextareanum] && aasworld->hidetraveltimes[nextareanum] < t) + { + continue; + } + aasworld->hidetraveltimes[nextareanum] = t; + // if the bestarea is this area, then it must be a longer route, so ignore it + if(bestarea == nextareanum) + { + bestarea = 0; + besttraveltime = 0; + } + // do this test now, so we can reject the route if it starts out too long + if(besttraveltime && t >= besttraveltime) + { + continue; + } + // + //avoid going near the enemy + ProjectPointOntoVector(enemyorigin, curupdate->start, reach->end, p); + for(j = 0; j < 3; j++) + { + if((p[j] > curupdate->start[j] + 0.1 && p[j] > reach->end[j] + 0.1) || + (p[j] < curupdate->start[j] - 0.1 && p[j] < reach->end[j] - 0.1)) + { + 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(enemytraveldist > 32 && dist2 < enemytraveldist && dist2 < 256) + { + continue; + } + // + VectorSubtract(reach->end, origin, v2); + if(enemytraveldist > 32 && DotProduct(v2, enemyVec) > enemytraveldist / 2) + { + continue; + } + // + VectorSubtract(enemyorigin, curupdate->start, v1); + dist1 = VectorLength(v1); + // + if(enemytraveldist > 32 && dist2 < dist1) + { + t += (dist1 - dist2) * 10; + // test it again after modifying it + if(besttraveltime && t >= besttraveltime) + { + continue; + } + } + // make sure the hide area doesn't have anyone else in it + if(AAS_IsEntityInArea(srcnum, -1, nextareanum)) + { + t += 1000; // avoid this path/area + //continue; + } + // + // if we weren't visible when starting, make sure we don't move into their view + if(enemyareanum && !startVisible && AAS_AreaVisible(enemyareanum, nextareanum)) + { + continue; + //t += 1000; + } + // + if(!besttraveltime || besttraveltime > t) + { + // + // if this area doesn't have a vis list, ignore it + if(aasworld->areavisibility[nextareanum]) + { + //if the nextarea is not visible from the enemy area + if(!AAS_AreaVisible(enemyareanum, nextareanum)) + { // now last of all, check that this area is a safe hiding spot + if((aasworld->visCache[nextareanum] == 2) || + (!aasworld->visCache[nextareanum] && + !botimport.BotVisibleFromPos(enemyorigin, enemynum, aasworld->areawaypoints[nextareanum], srcnum, + qfalse))) + { + aasworld->visCache[nextareanum] = 2; + besttraveltime = t; + bestarea = nextareanum; + } + else + { + aasworld->visCache[nextareanum] = 1; + } + } //end if + } + // + // getting down to here is bad for cpu usage + if(loopCount++ > MAX_HIDEAREA_LOOPS) + { + //botimport.Print(PRT_MESSAGE, "AAS_NearestHideArea: exceeded max loops, aborting\n" ); + continue; + } + // + // otherwise, add this to the list so we check is reachables + // disabled, this should only store the raw traveltime, not the adjusted time + //aasworld->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 + //botimport.Print(PRT_MESSAGE, "AAS_NearestHideArea: hidearea: %i, %i loops\n", bestarea, count ); + return bestarea; +} //end of the function AAS_NearestHideArea + +int BotFuzzyPointReachabilityArea(vec3_t origin); + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_FindAttackSpotWithinRange(int srcnum, int rangenum, int enemynum, float rangedist, int travelflags, float *outpos) +{ + int i, nextareanum, badtravelflags, numreach, bestarea; + unsigned short int t, besttraveltime, enemytraveltime; + aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; + aas_reachability_t *reach; + vec3_t srcorg, rangeorg, enemyorg; + int srcarea, rangearea, enemyarea; + unsigned short int srctraveltime; + int count = 0; + +#define MAX_ATTACKAREA_LOOPS 200 + static float lastTime; + + // + // RF, currently doesn't work with multiple AAS worlds, so only enable for the default world + //if (aasworld != aasworlds) return 0; + // + // don't run this more than once per frame + if(lastTime == AAS_Time()) + { + return 0; + } + lastTime = AAS_Time(); + // + if(!aasworld->hidetraveltimes) + { + aasworld->hidetraveltimes = (unsigned short int *)GetClearedMemory(aasworld->numareas * sizeof(unsigned short int)); + } + else + { + memset(aasworld->hidetraveltimes, 0, aasworld->numareas * sizeof(unsigned short int)); + } //end else + // + if(!aasworld->visCache) + { + aasworld->visCache = (byte *) GetClearedMemory(aasworld->numareas * sizeof(byte)); + } + else + { + memset(aasworld->visCache, 0, aasworld->numareas * sizeof(byte)); + } //end else + // + AAS_EntityOrigin(srcnum, srcorg); + AAS_EntityOrigin(rangenum, rangeorg); + AAS_EntityOrigin(enemynum, enemyorg); + // + srcarea = BotFuzzyPointReachabilityArea(srcorg); + rangearea = BotFuzzyPointReachabilityArea(rangeorg); + enemyarea = BotFuzzyPointReachabilityArea(enemyorg); + // + besttraveltime = 0; + bestarea = 0; + enemytraveltime = AAS_AreaTravelTimeToGoalArea(srcarea, srcorg, enemyarea, travelflags); + // + badtravelflags = ~travelflags; + // + curupdate = &aasworld->areaupdate[rangearea]; + curupdate->areanum = rangearea; + VectorCopy(rangeorg, curupdate->start); + curupdate->areatraveltimes = aasworld->areatraveltimes[srcarea][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 current list, flip the lists + 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(aasworld->travelflagfortype[reach->traveltype] & badtravelflags) + { + continue; + } + // + if(AAS_AreaContentsTravelFlag(reach->areanum) & badtravelflags) + { + continue; + } + // dont pass through ladder areas + if(aasworld->areasettings[reach->areanum].areaflags & AREA_LADDER) + { + continue; + } + // + if(aasworld->areasettings[reach->areanum].areaflags & AREA_DISABLED) + { + continue; + } + //number of the area the reachability leads to + nextareanum = reach->areanum; + // if this moves us into the enemies area, skip it + if(nextareanum == enemyarea) + { + continue; + } + // if we've already been to this area + if(aasworld->hidetraveltimes[nextareanum]) + { + continue; + } + //time already travelled plus the traveltime through + //the current area plus the travel time from the reachability + if(count++ > MAX_ATTACKAREA_LOOPS) + { + //botimport.Print(PRT_MESSAGE, "AAS_FindAttackSpotWithinRange: exceeded max loops, aborting\n" ); + if(bestarea) + { + VectorCopy(aasworld->areawaypoints[bestarea], outpos); + } + return bestarea; + } + t = curupdate->tmptraveltime + + AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->start) + reach->traveltime; + // + // if it's too far from rangenum, ignore + if(Distance(rangeorg, aasworld->areawaypoints[nextareanum]) > rangedist) + { + continue; + } + // + // find the traveltime from srcnum + srctraveltime = AAS_AreaTravelTimeToGoalArea(srcarea, srcorg, nextareanum, travelflags); + // do this test now, so we can reject the route if it starts out too long + if(besttraveltime && srctraveltime >= besttraveltime) + { + continue; + } + // + // if this area doesn't have a vis list, ignore it + if(aasworld->areavisibility[nextareanum]) + { + //if the nextarea can see the enemy area + if(AAS_AreaVisible(enemyarea, nextareanum)) + { // now last of all, check that this area is a good attacking spot + if((aasworld->visCache[nextareanum] == 2) || (!aasworld->visCache[nextareanum] && (count += 10) && // we are about to use lots of CPU time + botimport.BotCheckAttackAtPos(srcnum, enemynum, + aasworld-> + areawaypoints[nextareanum], + qfalse, qfalse))) + { + aasworld->visCache[nextareanum] = 2; + besttraveltime = srctraveltime; + bestarea = nextareanum; + } + else + { + aasworld->visCache[nextareanum] = 1; + } + } //end if + } + aasworld->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 for + } //end while +//botimport.Print(PRT_MESSAGE, "AAS_NearestHideArea: hidearea: %i, %i loops\n", bestarea, count ); + if(bestarea) + { + VectorCopy(aasworld->areawaypoints[bestarea], outpos); + } + return bestarea; +} //end of the function AAS_NearestHideArea + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_GetRouteFirstVisPos(vec3_t srcpos, vec3_t destpos, int travelflags, vec3_t retpos) +{ + int srcarea, destarea, travarea; + vec3_t travpos; + int ftraveltime, freachnum, lasttraveltime; + aas_reachability_t reach; + int loops = 0; + +#define MAX_GETROUTE_VISPOS_LOOPS 200 + // + // SRCPOS: enemy + // DESTPOS: self + // RETPOS: first area that is visible from destpos, in route from srcpos to destpos + srcarea = BotFuzzyPointReachabilityArea(srcpos); + if(!srcarea) + { + return qfalse; + } + destarea = BotFuzzyPointReachabilityArea(destpos); + if(!destarea) + { + return qfalse; + } + if(destarea == srcarea) + { + VectorCopy(srcpos, retpos); + return qtrue; + } + // + //if the srcarea can see the destarea + if(AAS_AreaVisible(srcarea, destarea)) + { + VectorCopy(srcpos, retpos); + return qtrue; + } + // if this area doesn't have a vis list, ignore it + if(!aasworld->areavisibility[destarea]) + { + return qfalse; + } + // + travarea = srcarea; + VectorCopy(srcpos, travpos); + lasttraveltime = -1; + while((loops++ < MAX_GETROUTE_VISPOS_LOOPS) && + AAS_AreaRouteToGoalArea(travarea, travpos, destarea, travelflags, &ftraveltime, &freachnum)) + { + if(lasttraveltime >= 0 && ftraveltime >= lasttraveltime) + { + return qfalse; // we may be in a loop + } + lasttraveltime = ftraveltime; + // + AAS_ReachabilityFromNum(freachnum, &reach); + if(reach.areanum == destarea) + { + VectorCopy(travpos, retpos); + return qtrue; + } + //if the reach area can see the destarea + if(AAS_AreaVisible(reach.areanum, destarea)) + { + VectorCopy(reach.end, retpos); + return qtrue; + } + // + travarea = reach.areanum; + VectorCopy(reach.end, travpos); + } + // + // unsuccessful + return qfalse; +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_ListAreasInRange(vec3_t srcpos, int srcarea, float range, int travelflags, vec3_t * outareas, int maxareas) +{ + int i, nextareanum, badtravelflags, numreach; + aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; + aas_reachability_t *reach; + int count = 0; + + // + if(!aasworld->hidetraveltimes) + { + aasworld->hidetraveltimes = (unsigned short int *)GetClearedMemory(aasworld->numareas * sizeof(unsigned short int)); + } + else + { + memset(aasworld->hidetraveltimes, 0, aasworld->numareas * sizeof(unsigned short int)); + } //end else + // + badtravelflags = ~travelflags; + // + curupdate = &aasworld->areaupdate[srcarea]; + curupdate->areanum = srcarea; + VectorCopy(srcpos, curupdate->start); + // GORDON: TEMP: FIXME: temp to stop crash + if(srcarea == 0) + { + return 0; + } + curupdate->areatraveltimes = aasworld->areatraveltimes[srcarea][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 current list, flip the lists + 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(aasworld->travelflagfortype[reach->traveltype] & badtravelflags) + { + continue; + } + // + if(AAS_AreaContentsTravelFlag(reach->areanum) & badtravelflags) + { + continue; + } + // dont pass through ladder areas + //if (aasworld->areasettings[reach->areanum].areaflags & AREA_LADDER) continue; + // + //if (aasworld->areasettings[reach->areanum].areaflags & AREA_DISABLED) continue; + //number of the area the reachability leads to + nextareanum = reach->areanum; + // if we've already been to this area + if(aasworld->hidetraveltimes[nextareanum]) + { + continue; + } + aasworld->hidetraveltimes[nextareanum] = 1; + // if it's too far from srcpos, ignore + if(Distance(srcpos, aasworld->areawaypoints[nextareanum]) > range) + { + continue; + } + // + // if visible from srcarea + if(!(aasworld->areasettings[reach->areanum].areaflags & AREA_LADDER) && + !(aasworld->areasettings[reach->areanum].areaflags & AREA_DISABLED) && (AAS_AreaVisible(srcarea, nextareanum))) + { + VectorCopy(aasworld->areawaypoints[nextareanum], outareas[count]); + count++; + if(count >= maxareas) + { + break; + } + } + // + nextupdate = &aasworld->areaupdate[nextareanum]; + nextupdate->areanum = nextareanum; + //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 for + } //end while + return count; +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AvoidDangerArea(vec3_t srcpos, int srcarea, vec3_t dangerpos, int dangerarea, float range, int travelflags) +{ + int i, nextareanum, badtravelflags, numreach, bestarea = 0; + aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; + aas_reachability_t *reach; + int bestTravel = 999999, t; + const int maxTime = 5000; + const int goodTime = 1000; + vec_t dangerDistance = 0; + + // + if(!aasworld->areavisibility) + { + return 0; + } + if(!srcarea) + { + return 0; + } + // + if(!aasworld->hidetraveltimes) + { + aasworld->hidetraveltimes = (unsigned short int *)GetClearedMemory(aasworld->numareas * sizeof(unsigned short int)); + } + else + { + memset(aasworld->hidetraveltimes, 0, aasworld->numareas * sizeof(unsigned short int)); + } //end else + // + badtravelflags = ~travelflags; + // + curupdate = &aasworld->areaupdate[srcarea]; + curupdate->areanum = srcarea; + VectorCopy(srcpos, curupdate->start); + curupdate->areatraveltimes = aasworld->areatraveltimes[srcarea][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; + + // Mad Doctor I, 11/3/2002. The source area never needs to be expanded + // again, so mark it as cut off + aasworld->hidetraveltimes[srcarea] = 1; + + //while there are updates in the current list, flip the lists + 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(aasworld->travelflagfortype[reach->traveltype] & badtravelflags) + { + continue; + } + // + if(AAS_AreaContentsTravelFlag(reach->areanum) & badtravelflags) + { + continue; + } + // dont pass through ladder areas + if(aasworld->areasettings[reach->areanum].areaflags & AREA_LADDER) + { + continue; + } + // + if(aasworld->areasettings[reach->areanum].areaflags & AREA_DISABLED) + { + continue; + } + //number of the area the reachability leads to + nextareanum = reach->areanum; + // if we've already been to this area + if(aasworld->hidetraveltimes[nextareanum]) + { + continue; + } + aasworld->hidetraveltimes[nextareanum] = 1; + // calc traveltime from srcpos + t = curupdate->tmptraveltime + + AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->start) + reach->traveltime; + if(t > maxTime) + { + continue; + } + if(t > bestTravel) + { + continue; + } + + // How far is it + dangerDistance = Distance(dangerpos, aasworld->areawaypoints[nextareanum]); + + // if it's safe from dangerpos + if(aasworld->areavisibility[nextareanum] && (dangerDistance > range)) + { + if(t < goodTime) + { + return nextareanum; + } + if(t < bestTravel) + { + bestTravel = t; + bestarea = nextareanum; + } + } + // + nextupdate = &aasworld->areaupdate[nextareanum]; + nextupdate->areanum = nextareanum; + //remember where we entered this area + VectorCopy(reach->end, nextupdate->start); + //if this update is not in the list yet + +// Mad Doctor I, 11/3/2002. The inlist field seems to not be inited properly for this function. +// It causes certain routes to be excluded unnecessarily, so I'm trying to do without it. +// Note that the hidetraveltimes array seems to cut off duplicates already. +// 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 for + } //end while + return bestarea; +} + + +// +// AAS_DangerPQInit() +// +// Description: Init the priority queue +// Written: 12/12/2002 +// +void AAS_DangerPQInit() +{ + // Local Variables //////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + +//@TEST. To test that the retreat algorithm is going to work, I've implemented +// a quicky, slow-ass PQ. This needs to be replaced with a heap implementation. + + // Init the distanceFromDanger array if needed + if(!aasworld->PQ_accumulator) + { + // Get memory for this array the safe way. + aasworld->PQ_accumulator = (unsigned short int *)GetClearedMemory(aasworld->numareas * sizeof(unsigned short int)); + + } // if (!aasworld->distanceFromDanger) ... + + // There are no items in the PQ right now + aasworld->PQ_size = 0; + +} + +// +// AAS_DangerPQInit() +// + + + +// +// AAS_DangerPQInsert +// +// Description: Put an area into the PQ. ASSUMES the dangerdistance for the +// area is set ahead of time. +// Written: 12/11/2002 +// +void AAS_DangerPQInsert( + // The area to insert + int areaNum) +{ + // Local Variables //////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + +//@TEST. To test that the retreat algorithm is going to work, I've implemented +// a quicky, slow-ass PQ. This needs to be replaced with a heap implementation. + + // Increment the count in the accum + aasworld->PQ_size++; + + // Put this one at the end + aasworld->PQ_accumulator[aasworld->PQ_size] = areaNum; + +} + +// +// AAS_DangerPQInsert +// + + + +// +// AAS_DangerPQEmpty +// +// Description: Is the Danger Priority Queue empty? +// Written: 12/11/2002 +// +qboolean AAS_DangerPQEmpty() +{ + // Local Variables //////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + +//@TEST. To test that the retreat algorithm is going to work, I've implemented +// a quicky, slow-ass PQ. This needs to be replaced with a heap implementation. + + // It's not empty if anything is in the accumulator + if(aasworld->PQ_size > 0) + { + return qfalse; + } + + return qtrue; +} + +// +// AAS_DangerPQEmpty +// + + +// +// AAS_DangerPQRemove +// +// Description: Pull the smallest distance area off of the danger Priority +// Queue. +// Written: 12/11/2002 +// +int AAS_DangerPQRemove() +{ + // Local Variables //////////////////////////////////////////////////////// + int j; + int nearest = 1; + int nearestArea = aasworld->PQ_accumulator[nearest]; + int nearestDistance = aasworld->distanceFromDanger[nearestArea]; + int distance; + int temp; + int currentArea; + + /////////////////////////////////////////////////////////////////////////// + +//@TEST. To test that the retreat algorithm is going to work, I've implemented +// a quicky, slow-ass PQ. This needs to be replaced with a heap implementation. + + // Just loop through the items in the PQ + for(j = 2; j <= aasworld->PQ_size; j++) + { + // What's the next area? + currentArea = aasworld->PQ_accumulator[j]; + + // What's the danerg distance of it + distance = aasworld->distanceFromDanger[currentArea]; + + // Is this element the best one? Top of the heap, so to speak + if(distance < nearestDistance) + { + // Save this one + nearest = j; + + // This has the best distance + nearestDistance = distance; + + // This one is the nearest region so far + nearestArea = currentArea; + + } // if (distance < nearestDistance)... + + } // for (j = 2; j <= aasworld->PQ_size; j++)... + + // Save out the old end of list + temp = aasworld->PQ_accumulator[aasworld->PQ_size]; + + // Put this where the old one was + aasworld->PQ_accumulator[nearest] = temp; + + // Decrement the count + aasworld->PQ_size--; + + return nearestArea; +} + +// +// AAS_DangerPQRemove +// + + +// +// AAS_DangerPQChange +// +// Description: We've changed the danger distance for this node, so change +// its place in the PQ if needed. +// Written: 12/11/2002 +// +void AAS_DangerPQChange( + // The area to change in the PQ + int areaNum) +{ + // Local Variables //////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + +//@TEST. To test that the retreat algorithm is going to work, I've implemented +// a quicky, slow-ass PQ. This needs to be replaced with a heap implementation. +// NOTHING NEEDS TO BE DONE HERE FOR THE SLOW-ASS PQ. + +} + +// +// AAS_DangerPQChange +// + + + + + + +//=========================================================================== +// +// AAS_CalculateDangerZones +// +// Description: +// Written: 12/11/2002 +// +//=========================================================================== +void AAS_CalculateDangerZones( + // Locations of the danger spots + int *dangerSpots, + // The number of danger spots + int dangerSpotCount, + // What is the furthest danger range we care about? (Everything further is safe) + float dangerRange, + // A safe distance to init distanceFromDanger to + unsigned short int definitelySafe) +{ + // Local Variables //////////////////////////////////////////////////////// + // Generic index used to loop through danger zones (and later, the reachabilities) + int i; + + // The area number of a danger spot + int dangerAreaNum; + + // What's the current AAS area we're measuring distance from danger to + int currentArea; + + // How many links come out of the current AAS area? + int numreach; + + // Number of the area the reachability leads to + int nextareanum; + + // Distance from current node to next node + float distanceFromCurrentNode; + + // Total distance from danger to next node + int dangerDistance; + + // The previous distance for this node + int oldDistance; + + // A link from the current node + aas_reachability_t *reach = NULL; + + /////////////////////////////////////////////////////////////////////////// + + // Initialize all of the starting danger zones. + for(i = 0; i < dangerSpotCount; i++) + { + // Get the area number of this danger spot + dangerAreaNum = dangerSpots[i]; + + // Set it's distance to 0, meaning it's 0 units to danger + aasworld->distanceFromDanger[dangerAreaNum] = 0; + + // Add the zone to the PQ. + AAS_DangerPQInsert(dangerAreaNum); + + } // for (i = 0; i < dangerSpotCount; i++)... + + // Go through the Priority Queue, pop off the smallest distance, and expand + // to the neighboring nodes. Stop when the PQ is empty. + while(!AAS_DangerPQEmpty()) + { + // Get the smallest distance in the PQ. + currentArea = AAS_DangerPQRemove(); + + // Check all reversed reachability links + numreach = aasworld->areasettings[currentArea].numreachableareas; + reach = &aasworld->reachability[aasworld->areasettings[currentArea].firstreachablearea]; + + // Loop through the neighbors to this node. + for(i = 0; i < numreach; i++, reach++) + { + // Number of the area the reachability leads to + nextareanum = reach->areanum; + + // How far was it from the last node to this one? + distanceFromCurrentNode = Distance(aasworld->areawaypoints[currentArea], aasworld->areawaypoints[nextareanum]); + + // Calculate the distance from danger to this neighbor along the + // current route. + dangerDistance = aasworld->distanceFromDanger[currentArea] + (int)distanceFromCurrentNode; + + // Skip this neighbor if the distance is bigger than we care about. + if(dangerDistance > dangerRange) + { + continue; + } + + // Store the distance from danger if it's smaller than any previous + // distance to this node (note that unvisited nodes are inited with + // a big distance, so this check will be satisfied). + if(dangerDistance < aasworld->distanceFromDanger[nextareanum]) + { + // How far was this node from danger before this visit? + oldDistance = aasworld->distanceFromDanger[nextareanum]; + + // Store the new value + aasworld->distanceFromDanger[nextareanum] = dangerDistance; + + // If the neighbor has been calculated already, see if we need to + // update the priority. + if(oldDistance != definitelySafe) + { + // We need to update the priority queue's position for this node + AAS_DangerPQChange(nextareanum); + + } // if (aasworld->distanceFromDanger[nextareanum] == definitelySafe)... + // Otherwise, insert the neighbor into the PQ. + else + { + // Insert this node into the PQ + AAS_DangerPQInsert(nextareanum); + + } // else ... + + } // if (dangerDistance < aasworld->distanceFromDanger[nextareanum])... + + } // for (i = 0; i < numreach; i++, reach++)... + + } // while (!AAS_DangerPQEmpty())... + + // At this point, all of the nodes within our danger range have their + // distance from danger calculated. + +} + +// +// AAS_CalculateDangerZones +// + + + +//=========================================================================== +// +// AAS_Retreat +// +// Use this to find a safe spot away from a dangerous situation/enemy. +// This differs from AAS_AvoidDangerArea in the following ways: +// * AAS_Retreat will return the farthest location found even if it does not +// exceed the desired minimum distance. +// * AAS_Retreat will give preference to nodes on the "safe" side of the danger +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Retreat( + // Locations of the danger spots (AAS area numbers) + int *dangerSpots, + // The number of danger spots + int dangerSpotCount, vec3_t srcpos, int srcarea, vec3_t dangerpos, int dangerarea, + // Min range from startpos + float range, + // Min range from danger + float dangerRange, int travelflags) +{ + int i, nextareanum, badtravelflags, numreach, bestarea = 0; + aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; + aas_reachability_t *reach; + int t; + const int maxTime = 5000; + +// const int goodTime = 1000; + vec_t dangerDistance = 0; + vec_t sourceDistance = 0; + float bestDistanceSoFar = 0; + + // Choose a safe distance to init distanceFromDanger to +// int definitelySafe = 1 + (int) range; + unsigned short int definitelySafe = 0xFFFF; + + // + if(!aasworld->areavisibility) + { + return 0; + } + + // Init the hide travel time if needed + if(!aasworld->hidetraveltimes) + { + aasworld->hidetraveltimes = (unsigned short int *)GetClearedMemory(aasworld->numareas * sizeof(unsigned short int)); + } + // Otherwise, it exists already, so just reset the values + else + { + memset(aasworld->hidetraveltimes, 0, aasworld->numareas * sizeof(unsigned short int)); + } //end else + + // Init the distanceFromDanger array if needed + if(!aasworld->distanceFromDanger) + { + // Get memory for this array the safe way. + aasworld->distanceFromDanger = (unsigned short int *)GetClearedMemory(aasworld->numareas * sizeof(unsigned short int)); + + } // if (!aasworld->distanceFromDanger) ... + + // Set all the values in the distanceFromDanger array to a safe value + memset(aasworld->distanceFromDanger, 0xFF, aasworld->numareas * sizeof(unsigned short int)); + + // Init the priority queue + AAS_DangerPQInit(); + + // Set up the distanceFromDanger array + AAS_CalculateDangerZones(dangerSpots, dangerSpotCount, dangerRange, definitelySafe); + + // + badtravelflags = ~travelflags; + // + curupdate = &aasworld->areaupdate[srcarea]; + curupdate->areanum = srcarea; + VectorCopy(srcpos, curupdate->start); + curupdate->areatraveltimes = aasworld->areatraveltimes[srcarea][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; + + // Mad Doctor I, 11/3/2002. The source area never needs to be expanded + // again, so mark it as cut off + aasworld->hidetraveltimes[srcarea] = 1; + + //while there are updates in the current list, flip the lists + 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(aasworld->travelflagfortype[reach->traveltype] & badtravelflags) + { + continue; + } + // + if(AAS_AreaContentsTravelFlag(reach->areanum) & badtravelflags) + { + continue; + } + // dont pass through ladder areas + if(aasworld->areasettings[reach->areanum].areaflags & AREA_LADDER) + { + continue; + } + // + if(aasworld->areasettings[reach->areanum].areaflags & AREA_DISABLED) + { + continue; + } + //number of the area the reachability leads to + nextareanum = reach->areanum; + // if we've already been to this area + if(aasworld->hidetraveltimes[nextareanum]) + { + continue; + } + aasworld->hidetraveltimes[nextareanum] = 1; + // calc traveltime from srcpos + t = curupdate->tmptraveltime + + AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->start) + reach->traveltime; + if(t > maxTime) + { + continue; + } + + // How far is it from a danger area? + dangerDistance = aasworld->distanceFromDanger[nextareanum]; + + // How far is it from our starting position? + sourceDistance = Distance(srcpos, aasworld->areawaypoints[nextareanum]); + + // If it's safe from dangerpos + if(aasworld->areavisibility[nextareanum] + && (sourceDistance > range) && ((dangerDistance > dangerRange) || (dangerDistance == definitelySafe))) + { + // Just use this area + return nextareanum; + } + + // In case we don't find a perfect one, save the best + if(dangerDistance > bestDistanceSoFar) + { + bestarea = nextareanum; + bestDistanceSoFar = dangerDistance; + + } // if (dangerDistance > bestDistanceSoFar)... + + // + nextupdate = &aasworld->areaupdate[nextareanum]; + nextupdate->areanum = nextareanum; + //remember where we entered this area + VectorCopy(reach->end, nextupdate->start); + //if this update is not in the list yet + + //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 for + } //end while + + return bestarea; +} + +/* +=================== +AAS_InitTeamDeath +=================== +*/ +void AAS_InitTeamDeath(void) +{ + if(aasworld->teamDeathTime) + { + FreeMemory(aasworld->teamDeathTime); + } + aasworld->teamDeathTime = GetClearedMemory(sizeof(int) * aasworld->numareas * 2); + if(aasworld->teamDeathCount) + { + FreeMemory(aasworld->teamDeathCount); + } + aasworld->teamDeathCount = GetClearedMemory(sizeof(byte) * aasworld->numareas * 2); + if(aasworld->teamDeathAvoid) + { + FreeMemory(aasworld->teamDeathAvoid); + } + aasworld->teamDeathAvoid = GetClearedMemory(sizeof(byte) * aasworld->numareas * 2); +} + +#define TEAM_DEATH_TIMEOUT 120.0 +#define TEAM_DEATH_AVOID_COUNT_SCALE 0.5 // so if there are 12 players, then if count reaches (12 * scale) then AVOID +#define TEAM_DEATH_RANGE 512.0 + +/* +=================== +AAS_UpdateTeamDeath +=================== +*/ +void AAS_UpdateTeamDeath(void) +{ + int i, j, k; + + // check for areas which have timed out, so we can stop avoiding them + + // for each area + for(i = 0; i < aasworld->numareas; i++) + { + // for each team + for(j = 0; j < 2; j++) + { + k = (aasworld->numareas * j) + i; + if(aasworld->teamDeathTime[k]) + { + if(aasworld->teamDeathTime[k] < AAS_Time() - TEAM_DEATH_TIMEOUT) + { + // this area has timed out + aasworld->teamDeathAvoid[k] = 0; + aasworld->teamDeathTime[k] = 0; + if(aasworld->teamDeathAvoid[k]) + { + // unmark this area + if(j == 0) + { + aasworld->areasettings[i].areaflags &= ~AREA_AVOID_AXIS; + } + else + { + aasworld->areasettings[i].areaflags &= ~AREA_AVOID_ALLIES; + } + //remove all routing cache involving this area + AAS_RemoveRoutingCacheUsingArea(i); + // recalculate the team flags that are used in this cluster + AAS_ClearClusterTeamFlags(i); + } + } + } + } + } +} + +/* +=================== +AAS_RecordTeamDeathArea +=================== +*/ +void AAS_RecordTeamDeathArea(vec3_t srcpos, int srcarea, int team, int teamCount, int travelflags) +{ + int i, nextareanum, badtravelflags, numreach, k; + aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; + aas_reachability_t *reach; + +// int count=0; +// + return; + // + badtravelflags = ~travelflags; + k = (aasworld->numareas * team) + srcarea; + // + curupdate = &aasworld->areaupdate[srcarea]; + curupdate->areanum = srcarea; + VectorCopy(srcpos, curupdate->start); + // + if(srcarea == 0) + { + return; + } + curupdate->areatraveltimes = aasworld->areatraveltimes[srcarea][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 current list, flip the lists + 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 tihs area has already been done + if(aasworld->teamDeathTime[reach->areanum] >= AAS_Time()) + { + continue; + } + aasworld->teamDeathTime[reach->areanum] = AAS_Time(); + // + //if an undesired travel type is used + if(aasworld->travelflagfortype[reach->traveltype] & badtravelflags) + { + continue; + } + // + if(AAS_AreaContentsTravelFlag(reach->areanum) & badtravelflags) + { + continue; + } + //number of the area the reachability leads to + nextareanum = reach->areanum; + // + // if it's too far from srcpos, ignore + if(Distance(curupdate->start, reach->end) + (float)curupdate->tmptraveltime > TEAM_DEATH_RANGE) + { + continue; + } + // + k = reach->areanum; + // mark this area + aasworld->teamDeathCount[k]++; + if(aasworld->teamDeathCount[k] > 100) + { + aasworld->teamDeathCount[k] = 100; // make sure it doesnt loop around + } + // + // see if this area is now to be avoided + if(!aasworld->teamDeathAvoid[k]) + { + if(aasworld->teamDeathCount[k] > (int)(TEAM_DEATH_AVOID_COUNT_SCALE * teamCount)) + { + // avoid this area + aasworld->teamDeathAvoid[k] = 1; + // mark this area + if(team == 0) + { + aasworld->areasettings[k].areaflags |= AREA_AVOID_AXIS; + } + else + { + aasworld->areasettings[k].areaflags |= AREA_AVOID_ALLIES; + } + //remove all routing cache involving this area + AAS_RemoveRoutingCacheUsingArea(k); + // recalculate the team flags that are used in this cluster + AAS_ClearClusterTeamFlags(k); + } + } + // + nextupdate = &aasworld->areaupdate[nextareanum]; + nextupdate->areanum = nextareanum; + //remember where we entered this area + VectorCopy(reach->end, nextupdate->start); + // calc the distance + nextupdate->tmptraveltime = (float)curupdate->tmptraveltime + Distance(curupdate->start, reach->end); + //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 for + } //end while + // + return; +} diff --git a/src/engine/botlib/be_aas_route.h b/src/engine/botlib/be_aas_route.h new file mode 100644 index 0000000000..f38d574006 --- /dev/null +++ b/src/engine/botlib/be_aas_route.h @@ -0,0 +1,84 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_route.h + * + * desc: AAS + * + * + *****************************************************************************/ + +#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_RoutingInfo(void); +#endif //AASINTERN + +//returns the travel flag for the given travel type +int AAS_TravelFlagForType(int traveltype); + +// +int AAS_AreaContentsTravelFlag(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); + +//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); + +void AAS_InitTeamDeath(void); +void AAS_RecordTeamDeathArea(vec3_t srcpos, int srcarea, int team, int teamCount, int travelflags); +void AAS_UpdateTeamDeath(void); diff --git a/src/engine/botlib/be_aas_routealt.c b/src/engine/botlib/be_aas_routealt.c new file mode 100644 index 0000000000..22da95a843 --- /dev/null +++ b/src/engine/botlib/be_aas_routealt.c @@ -0,0 +1,335 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_routealt.c + * + * desc: AAS + * + * + *****************************************************************************/ + +#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 + +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_AreaRouteToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags, int *traveltime, + int *reachnum); + +int AAS_AlternativeRouteGoals(vec3_t start, vec3_t goal, int travelflags, + aas_altroutegoal_t * altroutegoals, int maxaltroutegoals, int color) +{ +#ifndef ENABLE_ALTROUTING + return 0; +#else + int i, j, startareanum, goalareanum, bestareanum; + int numaltroutegoals, nummidrangeareas; + int starttime, goaltime, goaltraveltime; + float dist, bestdist; + vec3_t mid, dir; + int reachnum, time; + int a1, a2; + +/*#ifdef DEBUG + int startmillisecs; + + startmillisecs = Sys_MilliSeconds(); +#endif*/ + + startareanum = AAS_PointAreaNum(start); + if(!startareanum) + { + return 0; + } + goalareanum = AAS_PointAreaNum(goal); + if(!goalareanum) + { + VectorCopy(goal, dir); + dir[2] += 30; + goalareanum = AAS_PointAreaNum(dir); + if(!goalareanum) + { + return 0; + } + } + //travel time towards the goal area + goaltraveltime = AAS_AreaTravelTimeToGoalArea(startareanum, start, goalareanum, travelflags); + //clear the midrange areas + memset(midrangeareas, 0, (*aasworld).numareas * sizeof(midrangearea_t)); + numaltroutegoals = 0; + // + nummidrangeareas = 0; + // + for(i = 1; i < (*aasworld).numareas; i++) + { + // + if(!((*aasworld).areasettings[i].contents & AREACONTENTS_ROUTEPORTAL) && + !((*aasworld).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) + { + continue; + } + //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 > 500 + 3.0 * 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 > 500 + 3.0 * 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 + // make sure the route to the destination isn't in the same direction as the route to the source + if(!AAS_AreaRouteToGoalArea + (bestareanum, (*aasworld).areawaypoints[bestareanum], goalareanum, travelflags, &time, &reachnum)) + { + continue; + } + a1 = (*aasworld).reachability[reachnum].areanum; + if(!AAS_AreaRouteToGoalArea + (bestareanum, (*aasworld).areawaypoints[bestareanum], startareanum, travelflags, &time, &reachnum)) + { + continue; + } + a2 = (*aasworld).reachability[reachnum].areanum; + if(a1 == a2) + { + continue; + } + //now we've got an area for an alternative route + //FIXME: add alternative goal origin + VectorCopy((*aasworld).areawaypoints[bestareanum], 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 DEBUG + botimport.Print(PRT_MESSAGE, "alternative route goal area %d, numclusterareas = %d\n", bestareanum, numclusterareas); + if (color) + { + AAS_DrawPermanentCross((*aasworld).areas[bestareanum].center, 10, color); + } //end if + //AAS_ShowArea(bestarea, qtrue); +#endif*/ + //don't return more than the maximum alternative route goals + if(numaltroutegoals >= maxaltroutegoals) + { + break; + } + } //end for + //botimport.Print(PRT_MESSAGE, "%d alternative route goals\n", numaltroutegoals); +#ifdef 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 +} diff --git a/src/engine/botlib/be_aas_routealt.h b/src/engine/botlib/be_aas_routealt.h new file mode 100644 index 0000000000..3144f5238e --- /dev/null +++ b/src/engine/botlib/be_aas_routealt.h @@ -0,0 +1,51 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_routealt.h + * + * desc: AAS + * + * + *****************************************************************************/ + +#ifdef AASINTERN +void AAS_InitAlternativeRouting(void); +void AAS_ShutdownAlternativeRouting(void); +#endif //AASINTERN + + +int AAS_AlternativeRouteGoals(vec3_t start, vec3_t goal, int travelflags, + aas_altroutegoal_t * altroutegoals, int maxaltroutegoals, int color); diff --git a/src/engine/botlib/be_aas_routetable.c b/src/engine/botlib/be_aas_routetable.c new file mode 100644 index 0000000000..f6cbb049b6 --- /dev/null +++ b/src/engine/botlib/be_aas_routetable.c @@ -0,0 +1,1270 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +//=========================================================================== +// +// Name: be_aas_routetable.c +// Function: Area Awareness System, Route-table defines +// Programmer: Ridah +// Tab Size: 3 +//=========================================================================== + +#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_interface.h" +#include "be_aas_def.h" + +// ugly hack to turn off route-tables, can't find a way to check cvar's +int disable_routetable = 0; + +// this must be enabled for the route-tables to work, but it's not fully operational yet +#define CHECK_TRAVEL_TIMES +//#define DEBUG_ROUTETABLE +#define FILTERAREAS + +// enable this to use the built-in route-cache system to find the routes +#define USE_ROUTECACHE + +// enable this to disable Rocket/BFG Jumping, Grapple Hook +#define FILTER_TRAVEL + +// hmm, is there a cleaner way of finding out memory usage? +extern int totalmemorysize; +static int memorycount, cachememory; + +// globals to reduce function parameters +static unsigned short int *filtered_areas, childcount, num_parents; +static unsigned short int *rev_filtered_areas; + +// misc defines +unsigned short CRC_ProcessString(unsigned char *data, int length); + + +//=========================================================================== +// Memory debugging/optimization + +void *AAS_RT_GetClearedMemory(unsigned long size) +{ + void *ptr; + + memorycount += size; + + // ptr = GetClearedMemory(size); + ptr = GetClearedHunkMemory(size); + // Ryan - 01102k, need to use this, since the routetable calculations use up a lot of memory + // this will be a non-issue once we transfer the remnants of the routetable over to the aasworld + //ptr = malloc (size); + //memset (ptr, 0, size); + + return ptr; +} + +void AAS_RT_FreeMemory(void *ptr) +{ + int before; + + before = totalmemorysize; + + // FreeMemory( ptr ); + // Ryan - 01102k + free(ptr); + + memorycount -= before - totalmemorysize; +} + +void AAS_RT_PrintMemoryUsage() +{ +#ifdef AAS_RT_MEMORY_USAGE + + botimport.Print(PRT_MESSAGE, "\n"); + + // TODO: print the usage from each of the aas_rt_t lumps + +#endif +} + +//=========================================================================== + + +//=========================================================================== +// return the number of unassigned areas that are in the given area's visible list +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_RT_GetValidVisibleAreasCount(aas_area_buildlocalinfo_t * localinfo, aas_area_childlocaldata_t ** childlocaldata) +{ + int i, cnt; + + cnt = 1; // assume it can reach itself + + for(i = 0; i < localinfo->numvisible; i++) + { + if(childlocaldata[localinfo->visible[i]]) + { + continue; + } + + cnt++; + } + + return cnt; +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +static aas_rt_route_t **routetable; + +int AAS_AreaRouteToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags, int *traveltime, + int *reachnum); + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +void AAS_RT_CalcTravelTimesToGoalArea(int goalarea) +{ + int i; + + // TTimo: unused +// static int tfl = TFL_DEFAULT & ~(TFL_JUMPPAD|TFL_ROCKETJUMP|TFL_BFGJUMP|TFL_GRAPPLEHOOK|TFL_DOUBLEJUMP|TFL_RAMPJUMP|TFL_STRAFEJUMP|TFL_LAVA); //----(SA) modified since slime is no longer deadly +// static int tfl = TFL_DEFAULT & ~(TFL_JUMPPAD|TFL_ROCKETJUMP|TFL_BFGJUMP|TFL_GRAPPLEHOOK|TFL_DOUBLEJUMP|TFL_RAMPJUMP|TFL_STRAFEJUMP|TFL_SLIME|TFL_LAVA); + aas_rt_route_t *rt; + int reach, travel; + + for(i = 0; i < childcount; i++) + { + rt = &routetable[i][-1 + rev_filtered_areas[goalarea]]; + if(AAS_AreaRouteToGoalArea + (filtered_areas[i], (*aasworld).areas[filtered_areas[i]].center, goalarea, ~RTB_BADTRAVELFLAGS, &travel, &reach)) + { + rt->reachable_index = reach; + rt->travel_time = travel; + } + else + { + //rt->reachable_index = -1; + rt->travel_time = 0; + } + } +} + +//=========================================================================== +// calculate the initial route-table for each filtered area to all other areas +// +// FIXME: this isn't fully operational yet, for some reason not all routes are found +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RT_CalculateRouteTable(aas_rt_route_t ** parmroutetable) +{ + int i; + + routetable = parmroutetable; + + for(i = 0; i < childcount; i++) + { + AAS_RT_CalcTravelTimesToGoalArea(filtered_areas[i]); + } +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RT_AddParentLink(aas_area_childlocaldata_t * child, int parentindex, int childindex) +{ + aas_parent_link_t *oldparentlink; + + oldparentlink = child->parentlink; + + child->parentlink = (aas_parent_link_t *) AAS_RT_GetClearedMemory(sizeof(aas_parent_link_t)); + + child->parentlink->childindex = (unsigned short int)childindex; + child->parentlink->parent = (unsigned short int)parentindex; + child->parentlink->next = oldparentlink; +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RT_WriteShort(unsigned short int si, fileHandle_t fp) +{ + unsigned short int lsi; + + lsi = LittleShort(si); + botimport.FS_Write(&lsi, sizeof(lsi), fp); +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RT_WriteByte(int si, fileHandle_t fp) +{ + unsigned char uc; + + uc = si; + botimport.FS_Write(&uc, sizeof(uc), fp); +} + +//=========================================================================== +// writes the current route-table data to a .rtb file in tne maps folder +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RT_WriteRouteTable() +{ + int ident, version; + unsigned short crc_aas; + fileHandle_t fp; + char filename[MAX_QPATH]; + + // open the file for writing + Com_sprintf(filename, MAX_QPATH, "maps/%s.rtb", (*aasworld).mapname); + botimport.Print(PRT_MESSAGE, "\nsaving route-table to %s\n", filename); + botimport.FS_FOpenFile(filename, &fp, FS_WRITE); + if(!fp) + { + AAS_Error("Unable to open file: %s\n", filename); + return; + } + + // ident + ident = LittleLong(RTBID); + botimport.FS_Write(&ident, sizeof(ident), fp); + + // version + version = LittleLong(RTBVERSION); + botimport.FS_Write(&version, sizeof(version), fp); + + // crc + crc_aas = CRC_ProcessString((unsigned char *)(*aasworld).areas, sizeof(aas_area_t) * (*aasworld).numareas); + botimport.FS_Write(&crc_aas, sizeof(crc_aas), fp); + + // save the table data + + // children + botimport.FS_Write(&(*aasworld).routetable->numChildren, sizeof(int), fp); + botimport.FS_Write((*aasworld).routetable->children, (*aasworld).routetable->numChildren * sizeof(aas_rt_child_t), fp); + + // parents + botimport.FS_Write(&(*aasworld).routetable->numParents, sizeof(int), fp); + botimport.FS_Write((*aasworld).routetable->parents, (*aasworld).routetable->numParents * sizeof(aas_rt_parent_t), fp); + + // parentChildren + botimport.FS_Write(&(*aasworld).routetable->numParentChildren, sizeof(int), fp); + botimport.FS_Write((*aasworld).routetable->parentChildren, + (*aasworld).routetable->numParentChildren * sizeof(unsigned short int), fp); + + // visibleParents + botimport.FS_Write(&(*aasworld).routetable->numVisibleParents, sizeof(int), fp); + botimport.FS_Write((*aasworld).routetable->visibleParents, + (*aasworld).routetable->numVisibleParents * sizeof(unsigned short int), fp); + + // parentLinks + botimport.FS_Write(&(*aasworld).routetable->numParentLinks, sizeof(int), fp); + botimport.FS_Write((*aasworld).routetable->parentLinks, (*aasworld).routetable->numParentLinks * sizeof(aas_rt_parent_link_t), + fp); + + botimport.FS_FCloseFile(fp); + return; +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RT_DBG_Read(void *buf, int size, int fp) +{ + botimport.FS_Read(buf, size, fp); +} + +//=========================================================================== +// reads the given file, and creates the structures required for the route-table system +// +// Parameter: - +// Returns: qtrue if succesful, qfalse if not +// Changes Globals: - +//=========================================================================== +#define DEBUG_READING_TIME +qboolean AAS_RT_ReadRouteTable(fileHandle_t fp) +{ + int ident, version, i; + unsigned short int crc, crc_aas; + aas_rt_t *routetable; + aas_rt_child_t *child; + aas_rt_parent_t *parent; + aas_rt_parent_link_t *plink; + unsigned short int *psi; + + qboolean doswap; + +#ifdef DEBUG_READING_TIME + int pretime; + + pretime = Sys_MilliSeconds(); +#endif + + routetable = (*aasworld).routetable; + + doswap = (LittleLong(1) != 1); + + // check ident + AAS_RT_DBG_Read(&ident, sizeof(ident), fp); + ident = LittleLong(ident) + 0; // silence the warning + + if(ident != RTBID) + { + AAS_Error("File is not an RTB file\n"); + botimport.FS_FCloseFile(fp); + return qfalse; + } + + // check version + AAS_RT_DBG_Read(&version, sizeof(version), fp); + version = LittleLong(version) + 0; // silence the warning + + if(version != RTBVERSION) + { + AAS_Error("File is version %i not %i\n", version, RTBVERSION); + botimport.FS_FCloseFile(fp); + return qfalse; + } + + // read the CRC check on the AAS data + AAS_RT_DBG_Read(&crc, sizeof(crc), fp); + crc = LittleShort(crc) + (short)0; // silence the warning + + // calculate a CRC on the AAS areas + crc_aas = CRC_ProcessString((unsigned char *)(*aasworld).areas, sizeof(aas_area_t) * (*aasworld).numareas); + + if(crc != crc_aas) + { + AAS_Error("Route-table is from different AAS file, ignoring.\n"); + botimport.FS_FCloseFile(fp); + return qfalse; + } + + // read the route-table + + // children + botimport.FS_Read(&routetable->numChildren, sizeof(int), fp); + routetable->numChildren = LittleLong(routetable->numChildren); + routetable->children = (aas_rt_child_t *) AAS_RT_GetClearedMemory(routetable->numChildren * sizeof(aas_rt_child_t)); + botimport.FS_Read(routetable->children, routetable->numChildren * sizeof(aas_rt_child_t), fp); + child = &routetable->children[0]; + if(doswap) + { + for(i = 0; i < routetable->numChildren; i++, child++) + { + child->areanum = LittleShort(child->areanum); + child->numParentLinks = LittleLong(child->numParentLinks); + child->startParentLinks = LittleLong(child->startParentLinks); + } + } + + // parents + botimport.FS_Read(&routetable->numParents, sizeof(int), fp); + routetable->numParents = LittleLong(routetable->numParents); + routetable->parents = (aas_rt_parent_t *) AAS_RT_GetClearedMemory(routetable->numParents * sizeof(aas_rt_parent_t)); + botimport.FS_Read(routetable->parents, routetable->numParents * sizeof(aas_rt_parent_t), fp); + parent = &routetable->parents[0]; + if(doswap) + { + for(i = 0; i < routetable->numParents; i++, parent++) + { + parent->areanum = LittleShort(parent->areanum); + parent->numParentChildren = LittleLong(parent->numParentChildren); + parent->startParentChildren = LittleLong(parent->startParentChildren); + parent->numVisibleParents = LittleLong(parent->numVisibleParents); + parent->startVisibleParents = LittleLong(parent->startVisibleParents); + } + } + + // parentChildren + botimport.FS_Read(&routetable->numParentChildren, sizeof(int), fp); + routetable->numParentChildren = LittleLong(routetable->numParentChildren); + routetable->parentChildren = + (unsigned short int *)AAS_RT_GetClearedMemory(routetable->numParentChildren * sizeof(unsigned short int)); + botimport.FS_Read(routetable->parentChildren, routetable->numParentChildren * sizeof(unsigned short int), fp); + psi = &routetable->parentChildren[0]; + if(doswap) + { + for(i = 0; i < routetable->numParentChildren; i++, psi++) + { + *psi = LittleShort(*psi); + } + } + + // visibleParents + botimport.FS_Read(&routetable->numVisibleParents, sizeof(int), fp); + routetable->numVisibleParents = LittleLong(routetable->numVisibleParents); + routetable->visibleParents = + (unsigned short int *)AAS_RT_GetClearedMemory(routetable->numVisibleParents * sizeof(unsigned short int)); + botimport.FS_Read(routetable->visibleParents, routetable->numVisibleParents * sizeof(unsigned short int), fp); + psi = &routetable->visibleParents[0]; + if(doswap) + { + for(i = 0; i < routetable->numVisibleParents; i++, psi++) + { + *psi = LittleShort(*psi); + } + } + + // parentLinks + botimport.FS_Read(&routetable->numParentLinks, sizeof(int), fp); + routetable->numParentLinks = LittleLong(routetable->numParentLinks); + routetable->parentLinks = + (aas_rt_parent_link_t *) AAS_RT_GetClearedMemory(routetable->numParentLinks * sizeof(aas_rt_parent_link_t)); + botimport.FS_Read(routetable->parentLinks, routetable->numParentLinks * sizeof(aas_parent_link_t), fp); + plink = &routetable->parentLinks[0]; + if(doswap) + { + for(i = 0; i < routetable->numParentLinks; i++, plink++) + { + plink->childIndex = LittleShort(plink->childIndex); + plink->parent = LittleShort(plink->parent); + } + } + + // build the areaChildIndexes + routetable->areaChildIndexes = + (unsigned short int *)AAS_RT_GetClearedMemory((*aasworld).numareas * sizeof(unsigned short int)); + child = routetable->children; + for(i = 0; i < routetable->numChildren; i++, child++) + { + routetable->areaChildIndexes[child->areanum] = i + 1; + } + + botimport.Print(PRT_MESSAGE, "Total Parents: %d\n", routetable->numParents); + botimport.Print(PRT_MESSAGE, "Total Children: %d\n", routetable->numChildren); + botimport.Print(PRT_MESSAGE, "Total Memory Used: %d\n", memorycount); + +#ifdef DEBUG_READING_TIME + botimport.Print(PRT_MESSAGE, "Route-Table read time: %i\n", Sys_MilliSeconds() - pretime); +#endif + + botimport.FS_FCloseFile(fp); + return qtrue; +} + +int AAS_RT_NumParentLinks(aas_area_childlocaldata_t * child) +{ + aas_parent_link_t *plink; + int i; + + i = 0; + plink = child->parentlink; + while(plink) + { + i++; + plink = plink->next; + } + + return i; +} + +//=========================================================================== +// main routine to build the route-table +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CreateAllRoutingCache(void); + +void AAS_RT_BuildRouteTable(void) +{ + int i, j, k; + aas_area_t *srcarea; + aas_areasettings_t *srcsettings; + +// vec3_t vec; + unsigned int totalcount; + unsigned int noroutecount; + + aas_area_buildlocalinfo_t **area_localinfos; + aas_area_buildlocalinfo_t *localinfo; + + aas_area_childlocaldata_t **area_childlocaldata; + aas_area_childlocaldata_t *child; + + aas_area_parent_t *area_parents[MAX_PARENTS]; + aas_area_parent_t *thisparent; + + int bestchild, bestcount, bestparent, cnt; + + int memoryend; + + unsigned short int *visibleParents; + +#ifdef CHECK_TRAVEL_TIMES + aas_rt_route_t **filteredroutetable; + unsigned short int traveltime; +#endif + + fileHandle_t fp; + char filename[MAX_QPATH]; + +// not used anymore + return; + + // create the routetable in this aasworld + aasworld->routetable = (aas_rt_t *) AAS_RT_GetClearedMemory(sizeof(aas_rt_t)); + + // Try to load in a prepared route-table + Com_sprintf(filename, MAX_QPATH, "maps/%s.rtb", (*aasworld).mapname); + botimport.Print(PRT_MESSAGE, "\n---------------------------------\n"); + botimport.Print(PRT_MESSAGE, "\ntrying to load %s\n", filename); + botimport.FS_FOpenFile(filename, &fp, FS_READ); + if(fp) + { + // read in the table.. + if(AAS_RT_ReadRouteTable(fp)) + { + AAS_RT_PrintMemoryUsage(); + + botimport.Print(PRT_MESSAGE, "\nAAS Route-Table loaded.\n"); + botimport.Print(PRT_MESSAGE, "---------------------------------\n\n"); + return; + } + else + { + botimport.Print(PRT_MESSAGE, "\nUnable to load %s, building route-table..\n", filename); + } + } + else + { + botimport.Print(PRT_MESSAGE, "file not found, building route-table\n\n"); + } + + + botimport.Print(PRT_MESSAGE, "\n-------------------------------------\nRoute-table memory usage figures..\n\n"); + + totalcount = 0; + childcount = 0; + noroutecount = 0; + childcount = 0; + num_parents = 0; + + memorycount = 0; + cachememory = 0; + + filtered_areas = (unsigned short int *)AAS_RT_GetClearedMemory((*aasworld).numareas * sizeof(unsigned short int)); + rev_filtered_areas = (unsigned short int *)AAS_RT_GetClearedMemory((*aasworld).numareas * sizeof(unsigned short int)); + + // to speed things up, build a list of FILTERED areas first + // do this so we can check for filtered areas + AAS_CreateAllRoutingCache(); + for(i = 0; i < (*aasworld).numareas; i++) + { + srcarea = &(*aasworld).areas[i]; + srcsettings = &(*aasworld).areasettings[i]; + +#ifdef FILTERAREAS + if(!(srcsettings->areaflags & (AREA_USEFORROUTING))) + { + continue; + } + if(!(srcsettings->areaflags & (AREA_GROUNDED | AREA_LIQUID | AREA_LADDER))) + { + continue; + } +#endif + + rev_filtered_areas[i] = childcount + 1; + filtered_areas[childcount++] = (unsigned short int)i; + } + +#ifdef CHECK_TRAVEL_TIMES + // allocate and calculate the travel times + filteredroutetable = (aas_rt_route_t **) AAS_RT_GetClearedMemory(childcount * sizeof(aas_rt_route_t *)); + for(i = 0; i < childcount; i++) + filteredroutetable[i] = (aas_rt_route_t *) AAS_RT_GetClearedMemory(childcount * sizeof(aas_rt_route_t)); + + AAS_RT_CalculateRouteTable(filteredroutetable); + +#endif // CHECK_TRAVEL_TIMES + + // allocate for the temporary build local data + area_localinfos = (aas_area_buildlocalinfo_t **) AAS_RT_GetClearedMemory(childcount * sizeof(aas_area_buildlocalinfo_t *)); + + for(i = 0; i < childcount; i++) + { + srcarea = &(*aasworld).areas[filtered_areas[i]]; + srcsettings = &(*aasworld).areasettings[filtered_areas[i]]; + + // allocate memory for this area + area_localinfos[i] = (aas_area_buildlocalinfo_t *) AAS_RT_GetClearedMemory(sizeof(aas_area_buildlocalinfo_t)); + localinfo = area_localinfos[i]; + + for(j = 0; j < childcount; j++) + { + if(i == j) + { + continue; + } + +#ifdef CHECK_TRAVEL_TIMES + + // make sure travel time is reasonable + // Get the travel time from i to j + traveltime = (int)filteredroutetable[i][j].travel_time; + + if(!traveltime) + { + noroutecount++; + continue; + } + if(traveltime > MAX_LOCALTRAVELTIME) + { + continue; + } + +#endif // CHECK_TRAVEL_TIMES + + // Add it to the list + localinfo->visible[localinfo->numvisible++] = j; + totalcount++; + + if(localinfo->numvisible >= MAX_VISIBLE_AREAS) + { + botimport.Print(PRT_MESSAGE, "MAX_VISIBLE_AREAS exceeded, lower MAX_VISIBLE_RANGE\n"); + break; + } + } + } + + // now calculate the best list of locale's + + // allocate for the long-term child data + area_childlocaldata = + (aas_area_childlocaldata_t **) AAS_RT_GetClearedMemory(childcount * sizeof(aas_area_childlocaldata_t *)); + + for(i = 0; i < childcount; i++) + { + area_childlocaldata[i] = (aas_area_childlocaldata_t *) AAS_RT_GetClearedMemory(sizeof(aas_area_childlocaldata_t)); + area_childlocaldata[i]->areanum = filtered_areas[i]; + } + + while(1) + { + bestchild = -1; + bestcount = 99999; + + // find the area with the least number of visible areas + for(i = 0; i < childcount; i++) + { + if(area_childlocaldata[i]->parentlink) + { + continue; // already has been allocated to a parent + + } + cnt = AAS_RT_GetValidVisibleAreasCount(area_localinfos[i], area_childlocaldata); + + if(cnt < bestcount) + { + bestcount = area_localinfos[i]->numvisible; + bestchild = i; + } + } + + if(bestchild < 0) + { + break; // our job is done + + + } + localinfo = area_localinfos[bestchild]; + + + // look through this area's list of visible areas, and pick the one with the most VALID visible areas + bestparent = bestchild; + + for(i = 0; i < localinfo->numvisible; i++) + { + if(area_childlocaldata[localinfo->visible[i]]->parentlink) + { + continue; // already has been allocated to a parent + + } + // calculate how many of children are valid + cnt = AAS_RT_GetValidVisibleAreasCount(area_localinfos[localinfo->visible[i]], area_childlocaldata); + + if(cnt > bestcount) + { + bestcount = cnt; + bestparent = localinfo->visible[i]; + } + } + + // now setup this parent, and assign all it's children + localinfo = area_localinfos[bestparent]; + + // we use all children now, not just valid ones + bestcount = localinfo->numvisible; + + area_parents[num_parents] = (aas_area_parent_t *) AAS_RT_GetClearedMemory(sizeof(aas_area_parent_t)); + thisparent = area_parents[num_parents]; + + thisparent->areanum = filtered_areas[bestparent]; + thisparent->children = + (unsigned short int *)AAS_RT_GetClearedMemory((localinfo->numvisible + 1) * sizeof(unsigned short int)); + + // first, add itself to the list (yes, a parent is a child of itself) + child = area_childlocaldata[bestparent]; + AAS_RT_AddParentLink(child, num_parents, thisparent->numchildren); + thisparent->children[thisparent->numchildren++] = filtered_areas[bestparent]; + + // loop around all the parent's visible list, and make them children if they're aren't already assigned to a parent + for(i = 0; i < localinfo->numvisible; i++) + { + // create the childlocaldata + child = area_childlocaldata[localinfo->visible[i]]; + + // Ridah, only one parent per child in the new system + if(child->parentlink) + { + continue; // already has been allocated to a parent + + } + if(child->areanum != thisparent->areanum) + { + AAS_RT_AddParentLink(child, num_parents, thisparent->numchildren); + thisparent->children[thisparent->numchildren++] = filtered_areas[localinfo->visible[i]]; + } + } + + // now setup the list of children and the route-tables + for(i = 0; i < thisparent->numchildren; i++) + { + child = area_childlocaldata[-1 + rev_filtered_areas[thisparent->children[i]]]; + localinfo = area_localinfos[-1 + rev_filtered_areas[thisparent->children[i]]]; + + child->parentlink->routeindexes = + (unsigned short int *)AAS_RT_GetClearedMemory(thisparent->numchildren * sizeof(unsigned short int)); + + // now setup the indexes + for(j = 0; j < thisparent->numchildren; j++) + { + // find this child in our list of visibles + if(j == child->parentlink->childindex) + { + continue; + } + + for(k = 0; k < localinfo->numvisible; k++) + { + if(thisparent->children[j] == filtered_areas[localinfo->visible[k]]) + { // found a match + child->parentlink->routeindexes[j] = (unsigned short int)k; + break; + } + } + + if(k == localinfo->numvisible) + { // didn't find it, so add it to our list + if(localinfo->numvisible >= MAX_VISIBLE_AREAS) + { + botimport.Print(PRT_MESSAGE, "MAX_VISIBLE_AREAS exceeded, lower MAX_VISIBLE_RANGE\n"); + } + else + { + localinfo->visible[localinfo->numvisible] = -1 + rev_filtered_areas[thisparent->children[j]]; + child->parentlink->routeindexes[j] = (unsigned short int)localinfo->numvisible; + localinfo->numvisible++; + } + } + } + } + + num_parents++; + } + + // place all the visible areas from each child, into their childlocaldata route-table + for(i = 0; i < childcount; i++) + { + localinfo = area_localinfos[i]; + child = area_childlocaldata[i]; + + child->numlocal = localinfo->numvisible; + child->localroutes = (aas_rt_route_t *) AAS_RT_GetClearedMemory(localinfo->numvisible * sizeof(aas_rt_route_t)); + + for(j = 0; j < localinfo->numvisible; j++) + { + child->localroutes[j] = filteredroutetable[i][localinfo->visible[j]]; + } + + child->parentroutes = (aas_rt_route_t *) AAS_RT_GetClearedMemory(num_parents * sizeof(aas_rt_route_t)); + + for(j = 0; j < num_parents; j++) + { + child->parentroutes[j] = filteredroutetable[i][-1 + rev_filtered_areas[area_parents[j]->areanum]]; + } + } + + // build the visibleParents lists + visibleParents = (unsigned short int *)AAS_RT_GetClearedMemory(num_parents * sizeof(unsigned short int)); + for(i = 0; i < num_parents; i++) + { + area_parents[i]->numVisibleParents = 0; + + for(j = 0; j < num_parents; j++) + { + if(i == j) + { + continue; + } + + if(!AAS_inPVS((*aasworld).areas[area_parents[i]->areanum].center, (*aasworld).areas[area_parents[j]->areanum].center)) + { + continue; + } + + visibleParents[area_parents[i]->numVisibleParents] = j; + area_parents[i]->numVisibleParents++; + } + + // now copy the list over to the current src area + area_parents[i]->visibleParents = + (unsigned short int *)AAS_RT_GetClearedMemory(area_parents[i]->numVisibleParents * sizeof(unsigned short int)); + memcpy(area_parents[i]->visibleParents, visibleParents, area_parents[i]->numVisibleParents * sizeof(unsigned short int)); + + } + AAS_RT_FreeMemory(visibleParents); + + // before we free the main childlocaldata, go through and assign the aas_area's to their appropriate childlocaldata + // this would require modification of the aas_area_t structure, so for now, we'll just place them in a global array, for external reference + +// aasworld->routetable->area_childlocaldata_list = (aas_area_childlocaldata_t **) AAS_RT_GetClearedMemory( (*aasworld).numareas * sizeof(aas_area_childlocaldata_t *) ); +// for (i=0; iroutetable->area_childlocaldata_list[filtered_areas[i]] = area_childlocaldata[i]; +// } + + // copy the list of parents to a global structure for now (should eventually go into the (*aasworld) structure +// aasworld->routetable->area_parents_global = (aas_area_parent_t **) AAS_RT_GetClearedMemory( num_parents * sizeof(aas_area_parent_t *) ); +// memcpy( aasworld->routetable->area_parents_global, area_parents, num_parents * sizeof(aas_area_parent_t *) ); + + // ................................................ + // Convert the data into the correct format + { + aas_rt_t *rt; + aas_rt_child_t *child; + aas_rt_parent_t *parent; + aas_rt_parent_link_t *plink; + unsigned short int *psi; + + aas_area_childlocaldata_t *chloc; + aas_area_parent_t *apar; + aas_parent_link_t *oplink; + + int localRoutesCount, parentRoutesCount, parentChildrenCount, visibleParentsCount, parentLinkCount, + routeIndexesCount; + + rt = (*aasworld).routetable; + localRoutesCount = 0; + parentRoutesCount = 0; + parentChildrenCount = 0; + visibleParentsCount = 0; + parentLinkCount = 0; + routeIndexesCount = 0; + + // areaChildIndexes + rt->areaChildIndexes = (unsigned short int *)AAS_RT_GetClearedMemory((*aasworld).numareas * sizeof(unsigned short int)); + for(i = 0; i < childcount; i++) + { + rt->areaChildIndexes[filtered_areas[i]] = i + 1; + } + + // children + rt->numChildren = childcount; + rt->children = (aas_rt_child_t *) AAS_RT_GetClearedMemory(rt->numChildren * sizeof(aas_rt_child_t)); + child = rt->children; + for(i = 0; i < childcount; i++, child++) + { + chloc = area_childlocaldata[i]; + + child->areanum = chloc->areanum; + child->numParentLinks = AAS_RT_NumParentLinks(chloc); + + child->startParentLinks = parentLinkCount; + + parentLinkCount += child->numParentLinks; + } + + // parents + rt->numParents = num_parents; + rt->parents = (aas_rt_parent_t *) AAS_RT_GetClearedMemory(rt->numParents * sizeof(aas_rt_parent_t)); + parent = rt->parents; + for(i = 0; i < num_parents; i++, parent++) + { + apar = area_parents[i]; + + parent->areanum = apar->areanum; + parent->numParentChildren = apar->numchildren; + parent->numVisibleParents = apar->numVisibleParents; + + parent->startParentChildren = parentChildrenCount; + parent->startVisibleParents = visibleParentsCount; + + parentChildrenCount += parent->numParentChildren; + visibleParentsCount += parent->numVisibleParents; + } + + // parentChildren + rt->numParentChildren = parentChildrenCount; + rt->parentChildren = (unsigned short int *)AAS_RT_GetClearedMemory(parentChildrenCount * sizeof(unsigned short int)); + psi = rt->parentChildren; + for(i = 0; i < num_parents; i++) + { + apar = area_parents[i]; + for(j = 0; j < apar->numchildren; j++, psi++) + { + *psi = apar->children[j]; + } + } + + // visibleParents + rt->numVisibleParents = visibleParentsCount; + rt->visibleParents = (unsigned short int *)AAS_RT_GetClearedMemory(rt->numVisibleParents * sizeof(unsigned short int)); + psi = rt->visibleParents; + for(i = 0; i < num_parents; i++) + { + apar = area_parents[i]; + for(j = 0; j < apar->numVisibleParents; j++, psi++) + { + *psi = apar->visibleParents[j]; + } + } + + // parentLinks + rt->numParentLinks = parentLinkCount; + rt->parentLinks = (aas_rt_parent_link_t *) AAS_RT_GetClearedMemory(parentLinkCount * sizeof(aas_rt_parent_link_t)); + plink = rt->parentLinks; + for(i = 0; i < childcount; i++) + { + chloc = area_childlocaldata[i]; + for(oplink = chloc->parentlink; oplink; plink++, oplink = oplink->next) + { + plink->childIndex = oplink->childindex; + plink->parent = oplink->parent; + } + } + + } + // ................................................ + + // write the newly created table + AAS_RT_WriteRouteTable(); + + + botimport.Print(PRT_MESSAGE, "Child Areas: %i\nTotal Parents: %i\nAverage VisAreas: %i\n", (int)childcount, num_parents, + (int)(childcount / num_parents)); + botimport.Print(PRT_MESSAGE, "NoRoute Ratio: %i%%\n", (int)((100.0 * noroutecount) / (1.0 * childcount * childcount))); + + memoryend = memorycount; + + // clear allocated memory + +// causes crashes in route-caching +//#ifdef USE_ROUTECACHE +// AAS_FreeRoutingCaches(); +//#endif + + for(i = 0; i < childcount; i++) + { + AAS_RT_FreeMemory(area_localinfos[i]); +#ifdef CHECK_TRAVEL_TIMES + AAS_RT_FreeMemory(filteredroutetable[i]); +#endif + } + + { + aas_parent_link_t *next, *trav; + + // kill the client areas + for(i = 0; i < childcount; i++) + { + // kill the parent links + next = area_childlocaldata[i]->parentlink; + // TTimo gcc: suggests () around assignment used as truth value + while((trav = next)) + { + next = next->next; + + AAS_RT_FreeMemory(trav->routeindexes); + AAS_RT_FreeMemory(trav); + + } + + AAS_RT_FreeMemory(area_childlocaldata[i]->localroutes); + AAS_RT_FreeMemory(area_childlocaldata[i]->parentroutes); + AAS_RT_FreeMemory(area_childlocaldata[i]); + } + + // kill the parents + for(i = 0; i < num_parents; i++) + { + AAS_RT_FreeMemory(area_parents[i]->children); + AAS_RT_FreeMemory(area_parents[i]->visibleParents); + AAS_RT_FreeMemory(area_parents[i]); + } + } + + AAS_RT_FreeMemory(area_localinfos); + AAS_RT_FreeMemory(area_childlocaldata); + AAS_RT_FreeMemory(filtered_areas); + AAS_RT_FreeMemory(rev_filtered_areas); +#ifdef CHECK_TRAVEL_TIMES + AAS_RT_FreeMemory(filteredroutetable); +#endif + + // check how much memory we've used, and intend to keep + AAS_RT_PrintMemoryUsage(); + + botimport.Print(PRT_MESSAGE, "Route-Table Permanent Memory Usage: %i\n", memorycount); + botimport.Print(PRT_MESSAGE, "Route-Table Calculation Usage: %i\n", memoryend + cachememory); + botimport.Print(PRT_MESSAGE, "---------------------------------\n"); +} + +//=========================================================================== +// free permanent memory used by route-table system +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RT_ShutdownRouteTable(void) +{ + if(!aasworld->routetable) + { + return; + } + + // free the dynamic lists + AAS_RT_FreeMemory(aasworld->routetable->areaChildIndexes); + AAS_RT_FreeMemory(aasworld->routetable->children); + AAS_RT_FreeMemory(aasworld->routetable->parents); + AAS_RT_FreeMemory(aasworld->routetable->parentChildren); + AAS_RT_FreeMemory(aasworld->routetable->visibleParents); +// AAS_RT_FreeMemory( aasworld->routetable->localRoutes ); +// AAS_RT_FreeMemory( aasworld->routetable->parentRoutes ); + AAS_RT_FreeMemory(aasworld->routetable->parentLinks); +// AAS_RT_FreeMemory( aasworld->routetable->routeIndexes ); +// AAS_RT_FreeMemory( aasworld->routetable->parentTravelTimes ); + + // kill the table + AAS_RT_FreeMemory(aasworld->routetable); + aasworld->routetable = NULL; +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_rt_parent_link_t *AAS_RT_GetFirstParentLink(aas_rt_child_t * child) +{ + return &aasworld->routetable->parentLinks[child->startParentLinks]; +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_rt_child_t *AAS_RT_GetChild(int areanum) +{ + int i; + + i = (int)aasworld->routetable->areaChildIndexes[areanum] - 1; + + if(i >= 0) + { + return &aasworld->routetable->children[i]; + } + else + { + return NULL; + } +} + +//=========================================================================== +// returns a route between the areas +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaRouteToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags, int *traveltime, + int *reachnum); +aas_rt_route_t *AAS_RT_GetRoute(int srcnum, vec3_t origin, int destnum) +{ +#define GETROUTE_NUMROUTES 64 + static aas_rt_route_t routes[GETROUTE_NUMROUTES]; // cycle through these, so we don't overlap + static int routeIndex = 0; + aas_rt_route_t *thisroute; + int reach, traveltime; + aas_rt_t *rt; + static int tfl = TFL_DEFAULT & ~(TFL_JUMPPAD | TFL_ROCKETJUMP | TFL_BFGJUMP | TFL_GRAPPLEHOOK | TFL_DOUBLEJUMP | TFL_RAMPJUMP | TFL_STRAFEJUMP | TFL_LAVA); //----(SA) modified since slime is no longer deadly + +// static int tfl = TFL_DEFAULT & ~(TFL_JUMPPAD|TFL_ROCKETJUMP|TFL_BFGJUMP|TFL_GRAPPLEHOOK|TFL_DOUBLEJUMP|TFL_RAMPJUMP|TFL_STRAFEJUMP|TFL_SLIME|TFL_LAVA); + + if(!(rt = aasworld->routetable)) + { // no route table present + return NULL; + } + + if(disable_routetable) + { + return NULL; + } + + if(++routeIndex >= GETROUTE_NUMROUTES) + { + routeIndex = 0; + } + + thisroute = &routes[routeIndex]; + + if(AAS_AreaRouteToGoalArea(srcnum, origin, destnum, tfl, &traveltime, &reach)) + { + thisroute->reachable_index = reach; + thisroute->travel_time = traveltime; + return thisroute; + } + else + { + return NULL; + } +} + +//=========================================================================== +// draws the route-table from src to dest +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#include "be_ai_goal.h" + +int BotGetReachabilityToGoal(vec3_t origin, int areanum, int entnum, + int lastgoalareanum, int lastareanum, + int *avoidreach, float *avoidreachtimes, int *avoidreachtries, + bot_goal_t * goal, int travelflags, int movetravelflags); + +void AAS_RT_ShowRoute(vec3_t srcpos, int srcnum, int destnum) +{ +#ifdef DEBUG +#define MAX_RT_AVOID_REACH 1 + AAS_ClearShownPolygons(); + AAS_ClearShownDebugLines(); + AAS_ShowAreaPolygons(srcnum, 1, qtrue); + AAS_ShowAreaPolygons(destnum, 4, qtrue); + { + static int lastgoalareanum, lastareanum; + static int avoidreach[MAX_RT_AVOID_REACH]; + static float avoidreachtimes[MAX_RT_AVOID_REACH]; + static int avoidreachtries[MAX_RT_AVOID_REACH]; + int reachnum; + bot_goal_t goal; + aas_reachability_t reach; + + goal.areanum = destnum; + VectorCopy(botlibglobals.goalorigin, goal.origin); + reachnum = BotGetReachabilityToGoal(srcpos, srcnum, -1, + lastgoalareanum, lastareanum, + avoidreach, avoidreachtimes, avoidreachtries, + &goal, TFL_DEFAULT | TFL_FUNCBOB, TFL_DEFAULT | TFL_FUNCBOB); + AAS_ReachabilityFromNum(reachnum, &reach); + AAS_ShowReachability(&reach); + } +#endif +} + +/* +================= +AAS_RT_GetHidePos + + "src" is hiding ent, "dest" is the enemy +================= +*/ +qboolean AAS_RT_GetHidePos(vec3_t srcpos, int srcnum, int srcarea, vec3_t destpos, int destnum, int destarea, vec3_t returnPos) +{ + return 0; +} + +/* +================= +AAS_RT_GetReachabilityIndex +================= +*/ +int AAS_RT_GetReachabilityIndex(int areanum, int reachIndex) +{ +// return (*aasworld).areasettings[areanum].firstreachablearea + reachIndex; + return reachIndex; +} diff --git a/src/engine/botlib/be_aas_routetable.h b/src/engine/botlib/be_aas_routetable.h new file mode 100644 index 0000000000..5fe1101d3a --- /dev/null +++ b/src/engine/botlib/be_aas_routetable.h @@ -0,0 +1,176 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +//=========================================================================== +// +// Name: be_aas_routetable.h +// Function: Area Awareness System, Route-table defines +// Programmer: Ridah +// Tab Size: 3 +//=========================================================================== + +#ifndef RT_DEFINED + +#define RT_DEFINED + +#define RTBID ( ( 'B' << 24 ) + ( 'T' << 16 ) + ( 'R' << 8 ) + 'X' ) +#define RTBVERSION 17 + +#define RTB_BADTRAVELFLAGS ( TFL_JUMPPAD | TFL_ROCKETJUMP | TFL_BFGJUMP | TFL_GRAPPLEHOOK | TFL_DOUBLEJUMP | TFL_RAMPJUMP | TFL_STRAFEJUMP | TFL_LAVA ) //----(SA) modified since slime is no longer deadly +//#define RTB_BADTRAVELFLAGS (TFL_JUMPPAD|TFL_ROCKETJUMP|TFL_BFGJUMP|TFL_GRAPPLEHOOK|TFL_DOUBLEJUMP|TFL_RAMPJUMP|TFL_STRAFEJUMP|TFL_SLIME|TFL_LAVA) + +#define MAX_VISIBLE_AREAS 1024 // going over this limit will result in excessive memory usage, try and keep RANGE low enough so this limit won't be reached +#define MAX_LOCALTRAVELTIME 60 // use this to tweak memory usage (reduces parent count, increases local count (and cpu usage) - find a balance) +#define MAX_PARENTS 8192 + +extern int disable_routetable; + +//.................................................................... +// Permanent structures (in order of highest to lowest count) +typedef struct +{ + unsigned short int reachable_index; // reachability index (from this area's first reachability link in the world) to head for to get to the destination + unsigned short int travel_time; // travel time (!) +} aas_rt_route_t; + +typedef struct +{ + unsigned short int parent; // parent we belong to + unsigned short int childIndex; // our index in the parent's list of children +// unsigned short int numRouteIndexes; +// int startRouteIndexes; +} aas_rt_parent_link_t; + +typedef struct +{ + unsigned short int areanum; +// int numLocalRoutes; +// int startLocalRoutes; +// int numParentRoutes; +// int startParentRoutes; + int numParentLinks; + int startParentLinks; +} aas_rt_child_t; + +typedef struct +{ + unsigned short int areanum; // out area number in the global list + int numParentChildren; + int startParentChildren; + int numVisibleParents; + int startVisibleParents; // list of other parents that we can see (used for fast hide/retreat checks) +// int startParentTravelTimes; +} aas_rt_parent_t; + +// this is what each aasworld attaches itself to +typedef struct +{ + unsigned short int *areaChildIndexes; // each aas area that is part of the Route-Table has a pointer here to their position in the list of children + + int numChildren; + aas_rt_child_t *children; + + int numParents; + aas_rt_parent_t *parents; + + int numParentChildren; + unsigned short int *parentChildren; + + int numVisibleParents; + unsigned short int *visibleParents; + +// int numLocalRoutes; +// aas_rt_route_t *localRoutes; // the list of routes to all other local areas + +// int numParentRoutes; +// unsigned char *parentRoutes; // reachability to each other parent, as an offset from our first reachability + + int numParentLinks; + aas_rt_parent_link_t *parentLinks; // links from each child to the parent's it belongs to + +// int numParentTravelTimes; +// unsigned short int *parentTravelTimes; // travel times between all parent areas + +// int numRouteIndexes; +// unsigned short int *routeIndexes; // each parentLink has a list within here, which + // contains the local indexes of each child that + // belongs to the parent, within the source child's + // localroutes +} aas_rt_t; + +//.................................................................... +// Temp structures used only during route-table contruction +typedef struct +{ + unsigned short int numvisible; // number of areas that are visible and within range + unsigned short int visible[MAX_VISIBLE_AREAS]; // list of area indexes of visible and within range areas +} aas_area_buildlocalinfo_t; + +typedef struct aas_parent_link_s +{ + unsigned short int parent; // parent we belong to + unsigned short int childindex; // our index in the parent's list of children + unsigned short int *routeindexes; // for this parent link, list the children that fall under that parent, and their associated indexes in our localroutes table + struct aas_parent_link_s *next; +} aas_parent_link_t; + +typedef struct +{ + unsigned short int areanum; + unsigned short int numlocal; + aas_parent_link_t *parentlink; // linked list of parents that we belong to + aas_rt_route_t *localroutes; // the list of routes to all other local areas + aas_rt_route_t *parentroutes; // the list of routes to all other parent areas +} aas_area_childlocaldata_t; + +typedef struct +{ + unsigned short int areanum; // out area number in the global list + unsigned short int numchildren; + unsigned short int *children; + unsigned short int numVisibleParents; + unsigned short int *visibleParents; // list of other parents that we can see (used for fast hide/retreat checks) +} aas_area_parent_t; + +#endif // RT_DEFINED + +//.................................................................... + +void AAS_RT_BuildRouteTable(void); +void AAS_RT_ShowRoute(vec3_t srcpos, int srcnum, int destnum); +aas_rt_route_t *AAS_RT_GetRoute(int srcnum, vec3_t origin, int destnum); +void AAS_RT_ShutdownRouteTable(void); +qboolean AAS_RT_GetHidePos(vec3_t srcpos, int srcnum, int srcarea, vec3_t destpos, int destnum, int destarea, + vec3_t returnPos); +int AAS_RT_GetReachabilityIndex(int areanum, int reachIndex); diff --git a/src/engine/botlib/be_aas_sample.c b/src/engine/botlib/be_aas_sample.c new file mode 100644 index 0000000000..a4920f0765 --- /dev/null +++ b/src/engine/botlib/be_aas_sample.c @@ -0,0 +1,1683 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_sample.c + * + * desc: AAS environment sampling + * + * + *****************************************************************************/ + +#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 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; + +//=========================================================================== +// +// 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}, {-18, -18, -24}, {-18, -18, -24} }; + vec3_t boxmaxs[3] = { {0, 0, 0}, {18, 18, 48}, {18, 18, 24} }; + + 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) + { + max_aaslinks = (int)4096; //LibVarValue("max_aaslinks", "4096"); + if(max_aaslinks < 0) + { + max_aaslinks = 0; + } + (*aasworld).linkheapsize = max_aaslinks; + (*aasworld).linkheap = (aas_link_t *) GetHunkMemory(max_aaslinks * sizeof(aas_link_t)); + } + else + { + // just clear the memory + memset((*aasworld).linkheap, 0, (*aasworld).linkheapsize * sizeof(aas_link_t)); + } + //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]; +} //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) + { + botimport.Print(PRT_FATAL, "empty aas link heap\n"); + return NULL; + } //end if + if((*aasworld).freelinks) + { + (*aasworld).freelinks = (*aasworld).freelinks->next_ent; + } + if((*aasworld).freelinks) + { + (*aasworld).freelinks->prev_ent = NULL; + } + 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; +} //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 inPoint) +{ + int nodenum; + vec_t dist; + aas_node_t *node; + aas_plane_t *plane = NULL; + vec3_t point; + +// aas_plane_t *closestPlane; +// aas_node_t *closestNode; +// float closestDist; +// static int recursion = 0; + + VectorCopy(inPoint, point); + + 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; + +//nodesearch: + +// recursion++; +// closestDist = 128.0f; +// closestPlane = NULL; + 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]; + } +/* // check for closest plane + if (dist > 0 && Q_fabs(dist) < Q_fabs(closestDist)) { + closestPlane = plane; + closestDist = dist; + closestNode = node; + } +*/ } //end while + if(!nodenum) + { +#ifdef AAS_SAMPLE_DEBUG + botimport.Print(PRT_MESSAGE, "in solid\n"); +#endif //AAS_SAMPLE_DEBUG +/* + // RF (HACK), if we failed, move us to the other side of the closest plane + if ((recursion < 10) && closestPlane) { + dist = closestDist; + node = closestNode; + plane = closestPlane; + if (dist > 0) { + VectorMA( point, -(dist+1), plane->normal, point ); + } else { + VectorMA( point, -(dist-1), plane->normal, point ); + } + // take the opposite side since we have moved the point there now + if (dist <= 0) nodenum = node->children[0]; + else nodenum = node->children[1]; + // + goto nodesearch; + //return AAS_PointAreaNum( point ); + } +*/ +// recursion = 0; + return 0; + } //end if +// recursion = 0; + return -nodenum; +} //end of the function AAS_PointAreaNum + +//=========================================================================== +// +// 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 + +//=========================================================================== +// +// 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); + + 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 + memset(&trace, 0, sizeof(aas_trace_t)); + trace.ent = ENTITYNUM_NONE; + + 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 = ENTITYNUM_NONE; + 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; + // Gordon: NOTE, uninitialized var: v1 + VectorSubtract(end, start, 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 = ENTITYNUM_NONE; + 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; + + // Gordon: NOTE, uninitialized var: v1 + VectorSubtract(end, start, 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 = ENTITYNUM_NONE; + 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 + + //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); + } + //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; + // + if(frac < 0) + { + frac = 0.001; //0 + } + else if(frac > 1) + { + frac = 0.999; //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.01)) + { + 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.01)) + { + 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; + } + + 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: - +//=========================================================================== +aas_plane_t *AAS_PlaneFromNum(int planenum) +{ + if(!(*aasworld).loaded) + { + return 0; + } + + return &(*aasworld).planes[planenum]; +} //end of the function AAS_PlaneFromNum + + +#ifndef BSPC + +/* +============= +AAS_BBoxAreas +============= +*/ +#define NUM_BBOXAREASCACHE 128 +#define BBOXAREASCACHE_MAXAREAS 128 + +typedef struct +{ + float lastUsedTime; + int numUsed; + vec3_t absmins, absmaxs; +#if AAS_MAX_AREAS <= 65536 + unsigned short areas[BBOXAREASCACHE_MAXAREAS]; // we can used shorts since AAS_MAX_AREAS < 65536 +#else + int areas[BBOXAREASCACHE_MAXAREAS]; +#endif + int numAreas; +} bboxAreasCache_t; + +bboxAreasCache_t bboxAreasCache[NUM_BBOXAREASCACHE]; + +int AAS_BBoxAreasCheckCache(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas) +{ + int i; + bboxAreasCache_t *cache; + + // is this absmins/absmax in the cache? + for(i = 0, cache = bboxAreasCache; i < NUM_BBOXAREASCACHE; i++, cache++) + { + if(VectorCompare(absmins, cache->absmins) && VectorCompare(absmaxs, cache->absmaxs)) + { + // found a match + break; + } + } + + if(i == NUM_BBOXAREASCACHE) + { + return 0; + } + + // use this cache + cache->lastUsedTime = AAS_Time(); + cache->numUsed++; + if(cache->numUsed > 99999) + { + cache->numUsed = 99999; // cap it so it doesn't loop back to 0 + } + if(cache->numAreas > maxareas) + { + for(i = 0; i < maxareas; i++) + areas[i] = (int)cache->areas[i]; + return maxareas; + } + else + { + memcpy(areas, cache->areas, sizeof(int) * cache->numAreas); + return cache->numAreas; + } +} + +void AAS_BBoxAreasAddToCache(vec3_t absmins, vec3_t absmaxs, int *areas, int numareas) +{ + int i; + bboxAreasCache_t *cache, *weakestLink = NULL; + + // find a free cache slot + for(i = 0, cache = bboxAreasCache; i < NUM_BBOXAREASCACHE; i++, cache++) + { + if(!cache->lastUsedTime) + { + break; + } + if(cache->lastUsedTime < AAS_Time() - 2.0) + { + break; // too old + } + + if(!weakestLink) + { + weakestLink = cache; + } + else + { + if(cache->numUsed < weakestLink->numUsed) + { + weakestLink = cache; + } + } + } + + if(i == NUM_BBOXAREASCACHE) + { + // overwrite the weakest link + cache = weakestLink; + } + + cache->lastUsedTime = AAS_Time(); + cache->numUsed = 1; + VectorCopy(absmins, cache->absmins); + VectorCopy(absmaxs, cache->absmaxs); + + if(numareas > BBOXAREASCACHE_MAXAREAS) + { + numareas = BBOXAREASCACHE_MAXAREAS; + } + + for(i = 0; i < numareas; i++) + { + cache->areas[i] = (unsigned short)areas[i]; + } +} + +int AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas) +{ + aas_link_t *linkedareas, *link; + int num; + + if((num = AAS_BBoxAreasCheckCache(absmins, absmaxs, areas, maxareas))) + { + return 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; + } + } + + AAS_UnlinkFromAreas(linkedareas); + + //record this result in the cache + AAS_BBoxAreasAddToCache(absmins, absmaxs, areas, num); + + return num; +} //end of the function AAS_BBoxAreas + +#else + +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 + +#endif + +/* +============= +AAS_AreaCenter +============= +*/ +void AAS_AreaCenter(int areanum, vec3_t center) +{ + if(areanum < 0 || areanum >= (*aasworld).numareas) + { + botimport.Print(PRT_ERROR, "AAS_AreaCenter: invalid areanum\n"); + return; + } + VectorCopy((*aasworld).areas[areanum].center, center); + return; +} //end of the function AAS_AreaCenter + +/* +============= +AAS_AreaWaypoint +============= +*/ +qboolean AAS_AreaWaypoint(int areanum, vec3_t center) +{ + if(areanum < 0 || areanum >= (*aasworld).numareas) + { + botimport.Print(PRT_ERROR, "AAS_AreaWaypoint: invalid areanum\n"); + return qfalse; + } + if(!(*aasworld).areawaypoints) + { + return qfalse; + } + if(VectorCompare((*aasworld).areawaypoints[areanum], vec3_origin)) + { + return qfalse; + } + VectorCopy((*aasworld).areawaypoints[areanum], center); + return qtrue; +} //end of the function AAS_AreaCenter diff --git a/src/engine/botlib/be_aas_sample.h b/src/engine/botlib/be_aas_sample.h new file mode 100644 index 0000000000..9262c2270f --- /dev/null +++ b/src/engine/botlib/be_aas_sample.h @@ -0,0 +1,85 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_aas_sample.h + * + * desc: AAS + * + * + *****************************************************************************/ + +#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 area the point is in +int AAS_PointAreaNum(vec3_t point); + +//returns the plane the given face is in +void AAS_FacePlane(int facenum, vec3_t normal, float *dist); + +int AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas); +void AAS_AreaCenter(int areanum, vec3_t center); +qboolean AAS_AreaWaypoint(int areanum, vec3_t center); diff --git a/src/engine/botlib/be_ai_char.c b/src/engine/botlib/be_ai_char.c new file mode 100644 index 0000000000..0ab5402e6e --- /dev/null +++ b/src/engine/botlib/be_ai_char.c @@ -0,0 +1,898 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_ai_char.c + * + * desc: bot characters + * + * + *****************************************************************************/ + +#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]; + int 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 + PS_SetBaseFolder("botfiles"); + source = LoadSourceFile(charfile); + PS_SetBaseFolder(""); + 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, int skill) +{ + int handle; + + for(handle = 1; handle <= MAX_CLIENTS; handle++) + { + if(!botcharacters[handle]) + { + continue; + } + if(strcmp(botcharacters[handle]->filename, charfile) == 0 && (skill < 0 || botcharacters[handle]->skill == skill)) + { + return handle; + } //end if + } //end for + return 0; +} //end of the function BotFindCachedCharacter + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadCachedCharacter(char *charfile, int skill, int reload) +{ + int handle, cachedhandle; + 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 %d from %s\n", skill, charfile); + return cachedhandle; + } //end if + } //end else + //try to load the character with the given skill + ch = BotLoadCharacterFromFile(charfile, skill); + if(ch) + { + botcharacters[handle] = ch; + // + //botimport.Print(PRT_MESSAGE, "loaded skill %d from %s\n", skill, charfile); +#ifdef DEBUG + if(bot_developer) + { + botimport.Print(PRT_MESSAGE, "skill %d loaded in %d msec from %s\n", skill, Sys_MilliSeconds() - starttime, charfile); + } //end if +#endif //DEBUG + return handle; + } //end if + // + botimport.Print(PRT_WARNING, "couldn't find skill %d in %s\n", skill, charfile); + // + if(!reload) + { + //try to load a cached default character with the given skill + cachedhandle = BotFindCachedCharacter("bots/default_c.c", skill); + if(cachedhandle) + { + botimport.Print(PRT_MESSAGE, "loaded cached default skill %d from %s\n", skill, charfile); + return cachedhandle; + } //end if + } //end if + //try to load the default character with the given skill + ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, skill); + if(ch) + { + botcharacters[handle] = ch; + //botimport.Print(PRT_MESSAGE, "loaded default skill %d from %s\n", skill, 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 %d 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 %d 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 %d 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 %d 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, int 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, int 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 - 1) / (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, int skill) +{ + int skill1, skill4, handle; + + //make sure the skill is in the valid range + if(skill < 1) + { + skill = 1; + } + else if(skill > 5) + { + skill = 5; + } + //skill 1, 4 and 5 should be available in the character files + if(skill == 1 || skill == 4 || skill == 5) + { + return BotLoadCharacterSkill(charfile, skill); + } //end if + //check if there's a cached skill 2 or 3 + handle = BotFindCachedCharacter(charfile, skill); + if(handle) + { + //botimport.Print(PRT_MESSAGE, "loaded cached skill %d from %s\n", skill, charfile); + return handle; + } //end if + //load skill 1 and 4 + skill1 = BotLoadCharacterSkill(charfile, 1); + if(!skill1) + { + return 0; + } + skill4 = BotLoadCharacterSkill(charfile, 4); + if(!skill4) + { + return skill1; + } + //interpolate between 1 and 4 to create skill 2 or 3 + handle = BotInterpolateCharacters(skill1, skill4, 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/src/engine/botlib/be_ai_char.h b/src/engine/botlib/be_ai_char.h new file mode 100644 index 0000000000..6b7e155b37 --- /dev/null +++ b/src/engine/botlib/be_ai_char.h @@ -0,0 +1,66 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_ai_char.h + * + * desc: bot characters + * + * + *****************************************************************************/ + +//loads a bot character from a file +int BotLoadCharacter(char *charfile, int skill); + +//frees a bot character +void BotFreeCharacter(int character); + +//float characteristic +float Characteristic_Float(int character, int index); + +//bounded float characteristic +float Characteristic_BFloat(int character, int index, float min, float max); + +//integer characteristic +int Characteristic_Integer(int character, int index); + +//bounded integer characteristic +int Characteristic_BInteger(int character, int index, int min, int max); + +//string characteristic +void Characteristic_String(int character, int index, char *buf, int size); + +//free cached bot characters +void BotShutdownCharacters(void); diff --git a/src/engine/botlib/be_ai_chat.c b/src/engine/botlib/be_ai_chat.c new file mode 100644 index 0000000000..4c99743f66 --- /dev/null +++ b/src/engine/botlib/be_ai_chat.c @@ -0,0 +1,3422 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_ai_chat.c + * + * desc: bot chat AI + * + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +//#include "../server/server.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 + 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; + int inuse; + 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; + + cs = BotChatStateFromHandle(chatstate); + if(!cs) + { + return 0; + } + if(cs->firstmessage) + { + memcpy(cm, cs->firstmessage, sizeof(bot_consolemessage_t)); + cm->next = cm->prev = NULL; + 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 == '=') + { + 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++; + 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] == ' ') + { + 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 + 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++) + { + // +#ifdef IPHONE + // Weird memory smasher here: + if (pass && size) + { + ptr = (char *) malloc(size); + } +#else + if (pass && size) + { + ptr = (char *) GetClearedHunkMemory(size); + } +#endif // IPHONE + + // + 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) + { +#ifdef IPHONE + syn = (bot_synonymlist_t *) malloc(sizeof(bot_synonymlist_t)); +#else + syn = (bot_synonymlist_t *) ptr; + ptr += sizeof(bot_synonymlist_t); +#endif // IPHONE + 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) + { + 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 + size += sizeof(bot_synonym_t) + strlen(token.string) + 1; + if(pass) + { + synonym = (bot_synonym_t *) ptr; + ptr += sizeof(bot_synonym_t); + synonym->string = ptr; + ptr += strlen(token.string) + 1; + 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 + 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); + } + // + 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)) + { + if(token.type != TT_NAME) + { + SourceError(source, "unknown random %s", token.string); + FreeSource(source); + return NULL; + } //end if + size += sizeof(bot_randomlist_t) + strlen(token.string) + 1; + if(pass) + { + random = (bot_randomlist_t *) ptr; + ptr += sizeof(bot_randomlist_t); + random->string = ptr; + ptr += strlen(token.string) + 1; + 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, "}")) + { + if(!BotLoadChatMessage(source, chatmessagestring)) + { + FreeSource(source); + return NULL; + } //end if + size += sizeof(bot_randomstring_t) + strlen(chatmessagestring) + 1; + if(pass) + { + randomstring = (bot_randomstring_t *) ptr; + ptr += sizeof(bot_randomstring_t); + randomstring->string = ptr; + ptr += strlen(chatmessagestring) + 1; + 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 = rand() % 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) + { + // TTimo ? + // fprintf(fp, "%8d { "); + 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 >= 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; + + 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->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].ptr = strptr; + 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) + { + match->variables[lastvariable].length = strlen(match->variables[lastvariable].ptr); + } //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 pointers + for(i = 0; i < MAX_MATCHVARIABLES; i++) + match->variables[i].ptr = NULL; + // + 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].ptr) + { + if(match->variables[variable].length < size) + { + size = match->variables[variable].length + 1; + } + strncpy(buf, match->variables[variable].ptr, 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 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 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 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: - +//=========================================================================== +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; + + 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, "]")); + //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 + 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, "}")) + { + if(!BotLoadChatMessage(source, chatmessagestring)) + { + FreeSource(source); + return NULL; + } //end if + 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 += strlen(chatmessagestring) + 1; + //the number of chat messages increased + chattype->numchatmessages++; + } //end if + size += sizeof(bot_chatmessage_t) + strlen(chatmessagestring) + 1; + } //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 + // +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "loaded %s from %s\n", chatname, chatfile); +#endif + // + //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].inuse) + { + 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; + } + } + + PS_SetBaseFolder("botfiles"); + cs->chat = BotLoadInitialChat(chatfile, chatname); + PS_SetBaseFolder(""); + 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].chat = cs->chat; + Q_strncpyz(ichatdata[avail].chatname, chatname, sizeof(ichatdata[avail].chatname)); + Q_strncpyz(ichatdata[avail].filename, chatfile, sizeof(ichatdata[avail].filename)); + ichatdata[avail].inuse = qtrue; + } //end if + + return BLERR_NOERROR; +} //end of the function BotLoadChatFile + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotExpandChatMessage(char *outmessage, char *message, unsigned long mcontext, + bot_matchvariable_t * variables, 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 + ptr = variables[num].ptr; + if(ptr) + { + for(i = 0; i < 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_matchvariable_t * variables, 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, variables, 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; + bot_matchvariable_t variables[MAX_MATCHVARIABLES]; + 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 + // + memset(variables, 0, sizeof(variables)); + if(var0) + { + variables[0].ptr = var0; + variables[0].length = strlen(var0); + } + if(var1) + { + variables[1].ptr = var1; + variables[1].length = strlen(var1); + } + if(var2) + { + variables[2].ptr = var2; + variables[2].length = strlen(var2); + } + if(var3) + { + variables[3].ptr = var3; + variables[3].length = strlen(var3); + } + if(var4) + { + variables[4].ptr = var4; + variables[4].length = strlen(var4); + } + if(var5) + { + variables[5].ptr = var5; + variables[5].length = strlen(var5); + } + if(var6) + { + variables[6].ptr = var6; + variables[6].length = strlen(var6); + } + if(var7) + { + variables[7].ptr = var7; + variables[7].length = strlen(var7); + } + // + BotConstructChatMessage(cs, message, mcontext, variables, 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; + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if(!cs) + { + return qfalse; + } + 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 + //botnames is an exception + //if (!(key->flags & RCKFL_BOTNAMES)) found = qtrue; + } //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) + { + memcpy(&bestmatch, &match, sizeof(bot_match_t)); + bestchatmessage = m; + bestrchat = rchat; + bestpriority = rchat->priority; + } //end if + } //end if + } //end if + } //end for + if(bestchatmessage) + { + if(var0) + { + bestmatch.variables[0].ptr = var0; + bestmatch.variables[0].length = strlen(var0); + } + if(var1) + { + bestmatch.variables[1].ptr = var1; + bestmatch.variables[1].length = strlen(var1); + } + if(var2) + { + bestmatch.variables[2].ptr = var2; + bestmatch.variables[2].length = strlen(var2); + } + if(var3) + { + bestmatch.variables[3].ptr = var3; + bestmatch.variables[3].length = strlen(var3); + } + if(var4) + { + bestmatch.variables[4].ptr = var4; + bestmatch.variables[4].length = strlen(var4); + } + if(var5) + { + bestmatch.variables[5].ptr = var5; + bestmatch.variables[5].length = strlen(var5); + } + if(var6) + { + bestmatch.variables[6].ptr = var6; + bestmatch.variables[6].length = strlen(var6); + } + if(var7) + { + bestmatch.variables[7].ptr = var7; + bestmatch.variables[7].length = strlen(var7); + } + if(LibVarGetValue("bot_testrchat")) + { + for(m = bestrchat->firstchatmessage; m; m = m->next) + { + BotConstructChatMessage(cs, m->chatmessage, mcontext, bestmatch.variables, 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.variables, 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 client, 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 + { + if(sendto == CHAT_TEAM) + { + EA_SayTeam(client, cs->chatmessage); + } + else + { + EA_Say(client, cs->chatmessage); + } + } + //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) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if(!cs) + { + return; + } + 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 + + PS_SetBaseFolder("botfiles"); + 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 + PS_SetBaseFolder(""); + + 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].inuse) + { + FreeMemory(ichatdata[i].chat); + ichatdata[i].inuse = qfalse; + } //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/src/engine/botlib/be_ai_chat.h b/src/engine/botlib/be_ai_chat.h new file mode 100644 index 0000000000..a848451b09 --- /dev/null +++ b/src/engine/botlib/be_ai_chat.h @@ -0,0 +1,145 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_ai_chat.h + * + * desc: char AI + * + * + *****************************************************************************/ + +#define MAX_MESSAGE_SIZE 150 //limit in game dll +#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 + +//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 *ptr; + 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); + +//enters 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 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 client, 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 +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); diff --git a/src/engine/botlib/be_ai_gen.c b/src/engine/botlib/be_ai_gen.c new file mode 100644 index 0000000000..c21a5fd560 --- /dev/null +++ b/src/engine/botlib/be_ai_gen.c @@ -0,0 +1,171 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_ai_gen.c + * + * desc: genetic selection + * + * + *****************************************************************************/ + +#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 + 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/src/engine/botlib/be_ai_gen.h b/src/engine/botlib/be_ai_gen.h new file mode 100644 index 0000000000..70fded6eeb --- /dev/null +++ b/src/engine/botlib/be_ai_gen.h @@ -0,0 +1,44 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_ai_gen.h + * + * desc: genetic selection + * + * + *****************************************************************************/ + +int GeneticParentsAndChildSelection(int numranks, float *ranks, int *parent1, int *parent2, int *child); diff --git a/src/engine/botlib/be_ai_goal.c b/src/engine/botlib/be_ai_goal.c new file mode 100644 index 0000000000..7540351762 --- /dev/null +++ b/src/engine/botlib/be_ai_goal.c @@ -0,0 +1,1865 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_ai_goal.c + * + * desc: goal AI + * + * + *****************************************************************************/ + +#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 +//avoid goal time +#define AVOID_TIME 30 +//avoid dropped goal time +#define AVOIDDROPPED_TIME 5 +// +#define TRAVELTIME_SCALE 0.01 + +//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; + +typedef struct levelitem_s +{ + int number; //number of the level item + int iteminfo; //index into the item info + int notteam; //true if not in teamplay + int notfree; //true if not in ffa + int notsingle; //true if not in single + 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 ) (intptr_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}, + {0, 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]; + +//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 +// START Arnout changes, 28-08-2002. +// removed gametype, added single player +//int g_gametype; +qboolean g_singleplayer; + +// END Arnout changes, 28-08-2002. + +// Rafael gameskill +int g_gameskill; + +// done + +//======================================================================== +// +// 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 = 128; + LibVarSet("max_iteminfo", "128"); + } + + strncpy(path, filename, MAX_PATH); + 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]; + 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 *) GetMemory(max_levelitems * sizeof(levelitem_t)); + + for(i = 0; i < max_levelitems - 2; 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; + 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; + char classname[MAX_EPAIRKEY]; + vec3_t origin; + int ent; + itemconfig_t *ic; + levelitem_t *li; + + //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); + //FIXME: don't do this + // for now skip all floating entities + if(spawnflags & 1) + { + continue; + } + // + for(i = 0; i < ic->numiteminfo; i++) + { + if(!strcmp(classname, ic->iteminfo[i].classname)) + { + //get the origin of the item + if(AAS_VectorForBSPEpairKey(ent, "origin", origin)) + { + li = AllocLevelItem(); + if(!li) + { + return; + } + // + li->number = ++numlevelitems; + li->timeout = 0; + li->entitynum = 0; + // + AAS_IntForBSPEpairKey(ent, "notfree", &li->notfree); + AAS_IntForBSPEpairKey(ent, "notteam", &li->notteam); + AAS_IntForBSPEpairKey(ent, "notsingle", &li->notsingle); + //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); + //get the item goal area and goal origin + li->goalareanum = AAS_BestReachableArea(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs, li->goalorigin); + // + AddLevelItemToList(li); + } //end if + else + { + botimport.Print(PRT_ERROR, "item %s without origin\n", classname); + } //end else + break; + } //end if + } //end for + if(i >= ic->numiteminfo) + { + Log_Write("entity %s unknown item\r\n", classname); + } //end if + } //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; + } + memset(gs->avoidgoals, 0, MAX_AVOIDGOALS * sizeof(int)); + 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 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: - +//=========================================================================== +int BotGetLevelItemGoal(int index, char *name, bot_goal_t * goal) +{ + levelitem_t *li; + + if(!itemconfig) + { + return -1; + } + for(li = levelitems; li; li = li->next) + { + if(li->number <= index) + { + continue; + } + // +// START Arnout changes, 28-08-2002. +// removed gametype, added single player + //if (g_gametype == GT_SINGLE_PLAYER) { + if(g_singleplayer) + { + if(li->notsingle) + { + continue; + } + } + // Gordon: GT_TEAM no longer exists, switching for GT_WOLF +/* else + if (g_gametype >= GT_WOLF) { + if (li->notteam) continue; + } + else { + if (li->notfree) continue; + }*/ +// END Arnout changes, 28-08-2002. + // + 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; + //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: - +//=========================================================================== + +//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 level item isn't already stored + for(li = levelitems; li; li = li->next) + { + //if the model of the level item and the entity are different + if(ic->iteminfo[li->iteminfo].modelindex != modelindex) + { + continue; + } + //if the level item is linked to an entity + if(li->entitynum) + { + if(li->entitynum == ent) + { + VectorCopy(entinfo.origin, li->origin); + break; + } //end if + } //end if + else + { + //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; + //keep updating the entity 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); + //Log_Write("found item %s entity", ic->iteminfo[li->iteminfo].classname); + break; + } //end if + //else botimport.Print(PRT_MESSAGE, "item %s has no attached entity\n", + // ic->iteminfo[li->iteminfo].name); + } //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); + // + 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 +} //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++; + 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; + } + 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; + } + 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; + memset(&goal, 0, sizeof(bot_goal_t)); + //go through the items in the level + for(li = levelitems; li; li = li->next) + { +// START Arnout changes, 28-08-2002. +// removed gametype, added single player + //if (g_gametype == GT_SINGLE_PLAYER) { + if(g_singleplayer) + { + if(li->notsingle) + { + continue; + } + } + // Gordon: GT_TEAM no longer exists, switching for GT_WOLF +/* else + if (g_gametype >= GT_WOLF) { + if (li->notteam) continue; + } + else { + if (li->notfree) continue; + }*/ +// END Arnout changes, 28-08-2002. + //if the item is not in a possible goal area + if(!li->goalareanum) + { + continue; + } + //get the fuzzy weight function for this item + iteminfo = &ic->iteminfo[li->iteminfo]; + weightnum = gs->itemweightindex[iteminfo->number]; + if(weightnum < 0) + { + continue; + } + //if this goal is in the avoid goals + if(BotAvoidGoalTime(goalstate, li->number) > 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 += 1000; + } +#endif //DROPPEDWEIGHT + 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) + { + 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; + goal.iteminfo = bestitem->iteminfo; + //add the chosen goal to the goals to avoid for a while + avoidtime = iteminfo->respawntime * 0.5; + if(avoidtime < 10) + { + avoidtime = AVOID_TIME; + } + //if it's a dropped item + if(bestitem->timeout) + { + avoidtime = AVOIDDROPPED_TIME; + } + BotAddToAvoidGoals(gs, bestitem->number, avoidtime); + //push the goal on the stack + BotPushGoal(goalstate, &goal); + // +#ifdef DEBUG_AI_GOAL + if(bestitem->timeout) + { + botimport.Print(PRT_MESSAGE, "new ltg dropped item %s\n", ic->iteminfo[bestitem->iteminfo].classname); + } //end if + iteminfo = &ic->iteminfo[bestitem->iteminfo]; + botimport.Print(PRT_MESSAGE, "new ltg \"%s\"\n", iteminfo->classname); +#endif //DEBUG_AI_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; + memset(&goal, 0, sizeof(bot_goal_t)); + //go through the items in the level + for(li = levelitems; li; li = li->next) + { +// START Arnout changes, 28-08-2002. +// removed gametype, added single player + if(g_singleplayer) + { + if(li->notsingle) + { + continue; + } + } + // Gordon: GT_TEAM no longer exists, switching for GT_WOLF +/* else + if (g_gametype >= GT_WOLF) { + if (li->notteam) continue; + } + else { + if (li->notfree) continue; + }*/ +// END Arnout changes, 28-08-2002. + //if the item is in a possible goal area + if(!li->goalareanum) + { + continue; + } + //get the fuzzy weight function for this item + iteminfo = &ic->iteminfo[li->iteminfo]; + weightnum = gs->itemweightindex[iteminfo->number]; + if(weightnum < 0) + { + continue; + } + //if this goal is in the avoid goals + if(BotAvoidGoalTime(goalstate, li->number) > 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 += 1000; + } +#endif //DROPPEDWEIGHT + 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) + { + 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; + goal.iteminfo = bestitem->iteminfo; + //add the chosen goal to the goals to avoid for a while + avoidtime = iteminfo->respawntime * 0.5; + if(avoidtime < 10) + { + avoidtime = AVOID_TIME; + } + //if it's a dropped item + if(bestitem->timeout) + { + avoidtime = AVOIDDROPPED_TIME; + } + BotAddToAvoidGoals(gs, bestitem->number, avoidtime); + //push the goal on the stack + BotPushGoal(goalstate, &goal); + // +#ifdef DEBUG_AI_GOAL + if(bestitem->timeout) + { + botimport.Print(PRT_MESSAGE, "new nbg dropped item %s\n", ic->iteminfo[bestitem->iteminfo].classname); + } //end if + iteminfo = &ic->iteminfo[bestitem->iteminfo]; + botimport.Print(PRT_MESSAGE, "new nbg \"%s\"\n", iteminfo->classname); +#endif //DEBUG_AI_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; + } + 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 + PS_SetBaseFolder("botfiles"); + gs->itemweightconfig = ReadWeightConfig(filename); + PS_SetBaseFolder(""); + 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: - +//=========================================================================== +// START Arnout changes, 28-08-2002. +// removed gametype, added single player +int BotSetupGoalAI(qboolean singleplayer) +{ +// END Arnout changes, 28-08-2002. + char *filename; + + //check if teamplay is on +// START Arnout changes, 28-08-2002. +// removed gametype, added single player +// g_gametype = LibVarValue("g_gametype", "0"); + g_singleplayer = singleplayer; +// END Arnout changes, 28-08-2002. + //item configuration file + PS_SetBaseFolder("botfiles"); + filename = LibVarString("itemconfig", "items.c"); + //load the item configuration + itemconfig = LoadItemConfig(filename); + PS_SetBaseFolder(""); + if(!itemconfig) + { + botimport.Print(PRT_FATAL, "couldn't load item config\n"); + return BLERR_CANNOTLOADITEMCONFIG; + } //end if + //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/src/engine/botlib/be_ai_goal.h b/src/engine/botlib/be_ai_goal.h new file mode 100644 index 0000000000..3194546312 --- /dev/null +++ b/src/engine/botlib/be_ai_goal.h @@ -0,0 +1,174 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_ai_goal.h + * + * desc: goal AI + * + * + *****************************************************************************/ + +#define MAX_AVOIDGOALS 64 +#define MAX_GOALSTACK 8 + +#define GFL_NONE 0 +#define GFL_ITEM 1 +#define GFL_ROAM 2 +#define GFL_NOSLOWAPPROACH 4 +#define GFL_DEFEND_CLOSE 8 +#define GFL_LEADER 16 +#define GFL_DEBUGPATH 32 + +// Rafael gameskill +/*typedef enum { + GSKILL_EASY = 1, + GSKILL_MEDIUM, + GSKILL_MEDIUMHARD, // normal default level + GSKILL_HARD, + GSKILL_VERYHARD, + GSKILL_MAX // must always be last +} gameskill_t;*/ + +// bot goal urgency +#define BGU_LOW 0 +#define BGU_MEDIUM 1 +#define BGU_HIGH 2 +#define BGU_MAXIMUM 3 + +//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 + int urgency; //how urgent is the goal? should we be allowed to exit autonomy range to reach the goal? + int goalEndTime; // When is the shortest time this can end? +} 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 +void BotPushGoal(int goalstate, bot_goal_t * goal); + +//pop a goal +void BotPopGoal(int goalstate); + +//makes the bot's goal stack empty +void BotEmptyGoalStack(int goalstate); + +//dump the avoid goals +void BotDumpAvoidGoals(int goalstate); + +//dump the goal stack +void BotDumpGoalStack(int goalstate); + +//name of the goal +void BotGoalName(int number, char *name, int size); + +//get goal from top of stack +int BotGetTopGoal(int goalstate, bot_goal_t * goal); +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 +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); + +//get some info about a level item +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); + +//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 +// START Arnout changes, 28-08-2002. +// single player +int BotSetupGoalAI(qboolean singleplayer); + +// END Arnout changes, 28-08-2002. +//shut down the goal AI +void BotShutdownGoalAI(void); diff --git a/src/engine/botlib/be_ai_move.c b/src/engine/botlib/be_ai_move.c new file mode 100644 index 0000000000..c55babbc4d --- /dev/null +++ b/src/engine/botlib/be_ai_move.c @@ -0,0 +1,4521 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_ai_move.c + * + * desc: bot movement AI + * + * + *****************************************************************************/ + +#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 and MFL_WATERJUMP 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 + float lasttime; + 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_movestate_t; + +//used to avoid reachability links for some time after being used +#define AVOIDREACH +#define AVOIDREACH_TIME 4 //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 +//hook commands +#define CMD_HOOKOFF "hookoff" +#define CMD_HOOKON "hookon" +//weapon indexes for weapon jumping +#define WEAPONINDEX_ROCKET_LAUNCHER 5 +#define WEAPONINDEX_BFG 9 + +#define MODELTYPE_FUNC_PLAT 1 +#define MODELTYPE_FUNC_BOB 2 + +float sv_maxstep; +float sv_maxbarrier; +float sv_gravity; + +//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 + +// Ridah, provide a means of resetting the avoidreach, so if a bot stops moving, they don't avoid the area they were heading for +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotInitAvoidReach(int handle) +{ + bot_movestate_t *ms; + + ms = BotMoveStateFromHandle(handle); + if(!ms) + { + return; + } + + memset(ms->avoidreach, 0, sizeof(ms->avoidreach)); + memset(ms->avoidreachtries, 0, sizeof(ms->avoidreachtries)); + memset(ms->avoidreachtimes, 0, sizeof(ms->avoidreachtimes)); +} + +// done. + +//======================================================================== +// +// 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; + ms->areanum = initmove->areanum; + 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; + } +} //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 + +/* +================== +BotFirstReachabilityArea +================== +*/ +int BotFirstReachabilityArea(vec3_t origin, int *areas, int numareas, qboolean distCheck) +{ + int i, best = 0; + vec3_t center; + float bestDist, dist; + bsp_trace_t tr; + + // + bestDist = 999999; + for(i = 0; i < numareas; i++) + { + if(AAS_AreaReachability(areas[i])) + { + // make sure this area is visible + if(!AAS_AreaWaypoint(areas[i], center)) + { + AAS_AreaCenter(areas[i], center); + } + if(distCheck) + { + dist = VectorDistance(center, origin); + if(center[2] > origin[2]) + { + dist += 32 * (center[2] - origin[2]); + } + if(dist < bestDist) + { + tr = AAS_Trace(origin, NULL, NULL, center, -1, CONTENTS_SOLID | CONTENTS_PLAYERCLIP); + if(tr.fraction == 1.0 && !tr.startsolid && !tr.allsolid) + { + best = areas[i]; + bestDist = dist; + //if (dist < 128) { + // return best; + //} + } + } + } + else + { + tr = AAS_Trace(origin, NULL, NULL, center, -1, CONTENTS_SOLID | CONTENTS_PLAYERCLIP); + if(tr.fraction == 1.0 && !tr.startsolid && !tr.allsolid) + { + best = areas[i]; + break; + } + } + } + } + // + return best; +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotFuzzyPointReachabilityArea(vec3_t origin) +{ + int areanum, numareas, areas[100], bestarea = 0; //, i; + vec3_t end, start /*, ofs */ , mins, maxs; + + //float f; +#define BOTAREA_JIGGLE_DIST 256 // 32 // OUCH, hack for MP_BEACH which has lots of voids (mostly in water) + + areanum = AAS_PointAreaNum(origin); + if(!AAS_AreaReachability(areanum)) + { + areanum = 0; + } + if(areanum) + { + return areanum; + } + + // try a line trace from beneath to above + VectorCopy(origin, start); + VectorCopy(origin, end); + start[2] -= 30; + end[2] += 40; + numareas = AAS_TraceAreas(start, end, areas, NULL, 100); + if(numareas > 0) + { + bestarea = BotFirstReachabilityArea(origin, areas, numareas, qfalse); + } + if(bestarea) + { + return bestarea; + } + + // try a small box around the origin + maxs[0] = 4; + maxs[1] = 4; + maxs[2] = 4; + VectorSubtract(origin, maxs, mins); + VectorAdd(origin, maxs, maxs); + numareas = AAS_BBoxAreas(mins, maxs, areas, 100); + if(numareas > 0) + { + bestarea = BotFirstReachabilityArea(origin, areas, numareas, qtrue); + } + if(bestarea) + { + return bestarea; + } + + AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, mins, maxs); + VectorAdd(mins, origin, mins); + VectorAdd(maxs, origin, maxs); + numareas = AAS_BBoxAreas(mins, maxs, areas, 100); + if(numareas > 0) + { + bestarea = BotFirstReachabilityArea(origin, areas, numareas, qtrue); + } + if(bestarea) + { + return bestarea; + } +/* + // try half size first + for (f=0.1; f<=1.0; f+=0.45) { + VectorCopy( origin, end ); + end[2]+=80; + VectorCopy( origin, ofs ); + ofs[2]-=60; + for (i=0;i<2;i++) end[i]+=BOTAREA_JIGGLE_DIST*f; + for (i=0;i<2;i++) ofs[i]-=BOTAREA_JIGGLE_DIST*f; + + numareas = AAS_BBoxAreas(ofs, end, areas, 100); + if (numareas > 0) bestarea = BotFirstReachabilityArea(origin, areas, numareas); + if (bestarea) return bestarea; + } +*/ + return 0; +/* + 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); + // Ridah, increased this for Wolf larger bounding boxes + end[0] += x * 16;//8; + end[1] += y * 16;//8; + end[2] += z * 24;//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_OriginOfEntityWithModelNum(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_OriginOfEntityWithModelNum(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]; + + 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(!strcmp(classname, "func_bobbing")) + { + modeltypes[modelnum] = MODELTYPE_FUNC_BOB; + } + else if(!strcmp(classname, "func_plat")) + { + modeltypes[modelnum] = MODELTYPE_FUNC_PLAT; + } + } //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_AreaContentsTravelFlag(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: - +//=========================================================================== + +int BotGetReachabilityToGoal(vec3_t origin, int areanum, int entnum, + int lastgoalareanum, int lastareanum, + int *avoidreach, float *avoidreachtimes, int *avoidreachtries, + bot_goal_t * goal, int travelflags, int movetravelflags) +{ + int t, besttime, bestreachnum, reachnum; + aas_reachability_t reach; + qboolean useAvoidPass = qfalse; + + again: + + //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 + if(AAS_AreaDoNotEnterLarge(areanum) || AAS_AreaDoNotEnterLarge(goal->areanum)) + { + travelflags |= TFL_DONOTENTER_LARGE; + movetravelflags |= TFL_DONOTENTER_LARGE; + } //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 + int i; + + //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 + // RF, if this is a "useAvoidPass" then we should only accept avoidreach reachabilities + if((!useAvoidPass && i != MAX_AVOIDREACH && avoidreachtries[i] > AVOIDREACH_TRIES) + || (useAvoidPass && !(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_AreaContentsTravelFlag(reach.areanum) & ~travelflags) continue; + //if the travel isn't valid + if(!BotValidTravel(origin, &reach, movetravelflags)) + { + continue; + } + // if the area is disabled + if(!AAS_AreaReachability(reach.areanum)) + { + 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; + } + + // Ridah, if this sends us to a looped route, ignore it +// if (AAS_AreaTravelTimeToGoalArea(areanum, reach.start, goal->areanum, travelflags) + reach.traveltime == t) +// continue; + + //add the travel time towards the area + // Ridah, not sure why this was disabled, but it causes looped links in the route-cache + //t += reach.traveltime;// + AAS_AreaTravelTime(areanum, origin, reach.start); + t += reach.traveltime + AAS_AreaTravelTime(areanum, origin, reach.start); + + // Ridah, if there exists other entities in this area, avoid it +// if (reach.areanum != goal->areanum && AAS_IsEntityInArea( entnum, goal->entitynum, reach.areanum )) { +// t += 50; +// } + + //if the travel time is better than the ones already found + if(!besttime || t < besttime) + { + besttime = t; + bestreachnum = reachnum; + } //end if + } //end for + // + // RF, if we didnt find a route, then try again only looking through avoidreach reachabilities + if(!bestreachnum && !useAvoidPass) + { + useAvoidPass = qtrue; + goto again; + } + // + 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 == TRAVEL_TELEPORT) + { + return qtrue; + } + //don't add jump pad distances + if(reach.traveltype != TRAVEL_JUMPPAD && reach.traveltype != TRAVEL_ELEVATOR && reach.traveltype != TRAVEL_FUNCBOB) + { + if(BotAddToTarget(reach.start, reach.end, lookahead, &dist, target)) + { + return qtrue; + } + } //end if + reachnum = BotGetReachabilityToGoal(reach.end, reach.areanum, -1, + ms->lastgoalareanum, lastareanum, + ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries, + goal, travelflags, travelflags); + 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; + } + + 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, -1, + lastgoalareanum, lastareanum, + avoidreach, avoidreachtimes, avoidreachtries, goal, travelflags, travelflags); + 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_OriginOfEntityWithModelNum(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; + 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 - 8) + { + VectorCopy(trace.endpos, end); + end[2] -= 20; + if(AAS_PointContents(end) & (CONTENTS_WATER | CONTENTS_SLIME)) + { + break; //----(SA) modified since slime is no longer deadly + } +// 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; + //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) + { + 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) + { + 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 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.1, 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_ENTERLAVA | SE_HITGROUNDDAMAGE)) + { //----(SA) modified since slime is no longer deadly +// 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(Q_fabs(DotProduct(dir, up)) < 0.7) + { + mins[2] += sv_maxstep; //if the bot can step on + maxs[2] -= 10; //a little lower to avoid low ceiling + } //end if + VectorMA(ms->origin, 16, 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: - +//=========================================================================== +void BotClearMoveResult(bot_moveresult_t * moveresult) +{ + moveresult->failure = qfalse; + moveresult->type = 0; + moveresult->blocked = qfalse; + moveresult->blockentity = -1; + moveresult->traveltype = 0; + moveresult->flags = 0; +} //end of the function BotClearMoveResult + + +char *vtosf(const vec3_t v) +{ + static int index; + static char str[8][64]; + char *s; + + // use an array so that multiple vtos won't collide + s = str[index]; + index = (index + 1) & 7; + + Com_sprintf(s, 64, "(%f %f %f)", v[0], v[1], v[2]); + + return s; +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_Walk(bot_movestate_t * ms, aas_reachability_t * reach) +{ + float dist, speed; + vec3_t hordir; //, v1, v2, p; + bot_moveresult_t result; + + BotClearMoveResult(&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); + // + // Ridah, tweaked this +// if (dist < 10) + if(dist < 32) + { + //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); +/* + // if we are really close to the line, then move towards the center of the reach area + VectorCopy( reach->start, v1 ); + VectorCopy( reach->end, v2 ); + VectorCopy( ms->origin, p ); + v1[2] = 0; + v2[2] = 0; + p[2] = 0; + if (DistanceFromVectorSquared( p, v1, v2 ) < 4) { + if (!AAS_AreaWaypoint( reach->areanum, p )) + AAS_AreaCenter( reach->areanum, p ); + if (VectorDistance( ms->origin, p ) > 32) { + VectorSubtract( p, ms->origin, hordir ); + hordir[2] = 0; + dist = VectorNormalize(hordir); + } + } +*/ + } + else + { +//botimport.Print(PRT_MESSAGE, "BotTravel_Walk: NOT in range of reach (%i)\n", reach->areanum); + } //end if + //if going towards a crouch area + + // Ridah, some areas have a 0 presence (!?!) +// if (!(AAS_AreaPresenceType(reach->areanum) & PRESENCE_NORMAL)) + if((AAS_AreaPresenceType(reach->areanum) & PRESENCE_CROUCH) && !(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); +// +//botimport.Print(PRT_MESSAGE, "\nBotTravel_Walk: srcarea %i, rcharea %i, org %s, reachorg %s\n", ms->areanum, reach->areanum, vtosf(ms->origin), vtosf(reach->start) ); + // + 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 result; + + BotClearMoveResult(&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 result; + + BotClearMoveResult(&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 result; + + BotClearMoveResult(&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 < 12) + { + EA_Jump(ms->client); + + // Ridah, do the movement also, so we have momentum to get onto the barrier + hordir[0] = reach->end[0] - reach->start[0]; + hordir[1] = reach->end[1] - reach->start[1]; + hordir[2] = 0; + VectorNormalize(hordir); + + dist = 90; + speed = 400 - (360 - 4 * dist); + EA_Move(ms->client, hordir, speed); + // done. + } //end if + else + { + if(dist > 90) + { + dist = 90; + } + speed = 400 - (360 - 4 * 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, speed; + vec3_t hordir; + bot_moveresult_t result; + + BotClearMoveResult(&result); + //if near the top or going down + if(ms->velocity[2] < 250) + { + // Ridah, extend the end point a bit, so we strive to get over the ledge more + vec3_t end; + + VectorSubtract(reach->end, reach->start, end); + end[2] = 0; + VectorNormalize(end); + VectorMA(reach->end, 32, end, end); + hordir[0] = end[0] - ms->origin[0]; + hordir[1] = end[1] - ms->origin[1]; +// hordir[0] = reach->end[0] - ms->origin[0]; +// hordir[1] = reach->end[1] - ms->origin[1]; + // done. + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + BotCheckBlocked(ms, hordir, qtrue, &result); + // + if(dist > 60) + { + dist = 60; + } + speed = 400 - (400 - 6 * dist); + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + } + else + { + // hold crouch in case we are going for a crouch area + if(AAS_AreaPresenceType(reach->areanum) & PRESENCE_CROUCH) + { + EA_Crouch(ms->client); + } + } + // + 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 result; + + BotClearMoveResult(&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 result; + + BotClearMoveResult(&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 result; + + //botimport.Print(PRT_MESSAGE, "BotFinishTravel_WaterJump\n"); + BotClearMoveResult(&result); + //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 result; + + BotClearMoveResult(&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 + + // Ridah, tweaked this +#if 0 + if(dist < 48) + { + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; +#else + if((dist < 72) && (DotProduct(dir, hordir) < 0)) + { // walk in the direction of start -> end + //hordir[0] = reach->end[0] - ms->origin[0]; + //hordir[1] = reach->end[1] - ms->origin[1]; + //VectorNormalize( dir ); + //VectorMA( hordir, 48, dir, hordir ); + //hordir[2] = 0; + + VectorCopy(dir, hordir); +#endif + VectorNormalize(hordir); + // + if(reachhordist < 20) + { + speed = 100; + } //end if + else if(!AAS_HorizontalVelocityForJump(0, reach->start, reach->end, &speed)) + { + speed = 400; + } //end if + // looks better crouching off a ledge + EA_Crouch(ms->client); + } //end if + else + { + if(reachhordist < 20) + { + if(dist > 64) + { + dist = 64; + } + speed = 400 - (256 - 4 * dist); + } //end if + else + { + speed = 400; + // Ridah, tweaked this + if(dist < 128) + { + speed *= (dist / 128); + } + } //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 * 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 result; + + BotClearMoveResult(&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 + // + // looks better crouching off a ledge + EA_Crouch(ms->client); + 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 result; + + BotClearMoveResult(&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 result; + bsp_trace_t trace; + + BotClearMoveResult(&result); + // + 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 result; + + BotClearMoveResult(&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 < 12) + { +// 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 result; + + BotClearMoveResult(&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, pos, p, v1, v2, vec, right; + vec3_t origin = { 0, 0, 0 }; +// vec3_t up = {0, 0, 1}; + bot_moveresult_t result; + float dist, speed; + + // RF, heavily modified, wolf has different ladder movement + + BotClearMoveResult(&result); + // + // if it's a descend reachability + if(reach->start[2] > reach->end[2]) + { + if(!(ms->moveflags & MFL_ONGROUND)) + { + //botimport.Print(PRT_MESSAGE, "not on ground\n"); + // RF, wolf has different ladder movement + VectorSubtract(reach->end, reach->start, dir); + dir[2] = 0; + dist = VectorNormalize(dir); + //set the ideal view angles, facing the ladder up or down + viewdir[0] = dir[0]; + viewdir[1] = dir[1]; + viewdir[2] = 0; // straight forward goes up + if(dist >= 3.1) + { // vertical ladder -> ladder reachability has length 3, dont inverse in this case + VectorInverse(viewdir); + } + VectorNormalize(viewdir); + Vector2Angles(viewdir, result.ideal_viewangles); + //elemantary action + EA_Move(ms->client, origin, 0); + // RF, disabled this check, if we're not on ground, then it shouldn't be a problem, and the ladder flag is unreliable + //if (ms->moveflags & MFL_AGAINSTLADDER) { + //botimport.Print(PRT_MESSAGE, "against ladder, descending\n"); + EA_MoveBack(ms->client); // only move back if we are touching the ladder brush + // check for sideways adjustments to stay on the center of the ladder + VectorMA(ms->origin, 18, viewdir, p); + VectorCopy(reach->start, v1); + v1[2] = ms->origin[2]; + VectorCopy(reach->end, v2); + v2[2] = ms->origin[2]; + VectorSubtract(v2, v1, vec); + VectorNormalize(vec); + VectorMA(v1, -32, vec, v1); + VectorMA(v2, 32, vec, v2); + ProjectPointOntoVector(p, v1, v2, pos); + VectorSubtract(pos, p, vec); + if(VectorLength(vec) > 2) + { + AngleVectors(result.ideal_viewangles, NULL, right, NULL); + if(DotProduct(vec, right) > 0) + { + EA_MoveRight(ms->client); + } + else + { + EA_MoveLeft(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 top\n"); + // find a postion back away from the edge of the ladder + VectorSubtract(reach->end, reach->start, hordir); + hordir[2] = 0; + VectorNormalize(hordir); + VectorMA(reach->start, -24, hordir, pos); + VectorCopy(reach->end, v1); + v1[2] = pos[2]; + // project our position onto the vector + ProjectPointOntoVectorBounded(ms->origin, pos, v1, p); + VectorSubtract(p, ms->origin, dir); + //make sure the horizontal movement is large anough + VectorCopy(dir, hordir); + hordir[2] = 0; + dist = VectorNormalize(hordir); + if(dist < 64) + { // within range, go for the end + //botimport.Print(PRT_MESSAGE, "found top, moving towards ladder edge\n"); + VectorSubtract(reach->end, reach->start, dir); + //make sure the horizontal movement is large anough + dir[2] = 0; + VectorNormalize(dir); + //set the ideal view angles, facing the ladder up or down + viewdir[0] = dir[0]; + viewdir[1] = dir[1]; + viewdir[2] = 0; + VectorInverse(viewdir); + VectorNormalize(viewdir); + Vector2Angles(viewdir, result.ideal_viewangles); + result.flags |= MOVERESULT_MOVEMENTVIEW; + // if we are still on ground, then start moving backwards until we are in air + if((dist < 4) && (ms->moveflags & MFL_ONGROUND)) + { + //botimport.Print(PRT_MESSAGE, "close to edge, backing in slowly..\n"); + VectorSubtract(reach->end, ms->origin, vec); + vec[2] = 0; + VectorNormalize(vec); + EA_Move(ms->client, vec, 100); + VectorCopy(vec, result.movedir); + result.ideal_viewangles[PITCH] = 45; // face downwards + return result; + } + } + // + dir[0] = hordir[0]; + dir[1] = hordir[1]; + dir[2] = 0; + if(dist > 150) + { + dist = 150; + } + speed = 400 - (300 - 2 * dist); + //botimport.Print(PRT_MESSAGE, "speed = %.0f\n", speed); + EA_Move(ms->client, dir, speed); + } //end else + } + else + { + 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"); + // RF, wolf has different ladder movement + VectorSubtract(reach->end, reach->start, dir); + VectorNormalize(dir); + //set the ideal view angles, facing the ladder up or down + viewdir[0] = dir[0]; + viewdir[1] = dir[1]; + viewdir[2] = dir[2]; + if(dir[2] < 0) + { // going down, so face the other way (towards ladder) + VectorInverse(viewdir); + } + viewdir[2] = 0; // straight forward goes up + VectorNormalize(viewdir); + Vector2Angles(viewdir, result.ideal_viewangles); + //elemantary action + EA_Move(ms->client, origin, 0); + if(dir[2] < 0) + { // going down, so face the other way + EA_MoveBack(ms->client); + } + else + { + EA_MoveForward(ms->client); + } + // RF, against ladder code isn't completely accurate + //if (ms->moveflags & MFL_AGAINSTLADDER) { + // check for sideways adjustments to stay on the center of the ladder + VectorMA(ms->origin, 18, viewdir, p); + VectorCopy(reach->start, v1); + v1[2] = ms->origin[2]; + VectorCopy(reach->end, v2); + v2[2] = ms->origin[2]; + VectorSubtract(v2, v1, vec); + VectorNormalize(vec); + VectorMA(v1, -32, vec, v1); + VectorMA(v2, 32, vec, v2); + ProjectPointOntoVectorBounded(p, v1, v2, pos); + VectorSubtract(pos, p, vec); + if(VectorLength(vec) > 2) + { + AngleVectors(result.ideal_viewangles, NULL, right, NULL); + if(DotProduct(vec, right) > 0) + { + EA_MoveRight(ms->client); + } + else + { + EA_MoveLeft(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 base\n"); + // find a postion back away from the base of the ladder + VectorSubtract(reach->end, reach->start, hordir); + hordir[2] = 0; + VectorNormalize(hordir); + VectorMA(reach->start, -24, hordir, pos); + VectorCopy(reach->end, v1); + v1[2] = pos[2]; + // project our position onto the vector + ProjectPointOntoVectorBounded(ms->origin, pos, v1, p); + VectorSubtract(p, ms->origin, dir); + //make sure the horizontal movement is large anough + VectorCopy(dir, hordir); + hordir[2] = 0; + dist = VectorNormalize(hordir); + if(dist < 16) + { // within range, go for the end + //botimport.Print(PRT_MESSAGE, "found base, moving towards ladder top\n"); + VectorSubtract(reach->end, ms->origin, dir); + //make sure the horizontal movement is large anough + VectorCopy(dir, hordir); + hordir[2] = 0; + dist = VectorNormalize(hordir); + //set the ideal view angles, facing the ladder up or down + viewdir[0] = dir[0]; + viewdir[1] = dir[1]; + viewdir[2] = 0; + VectorNormalize(viewdir); + Vector2Angles(viewdir, result.ideal_viewangles); + result.flags |= MOVERESULT_MOVEMENTVIEW; + } + // + dir[0] = hordir[0]; + dir[1] = hordir[1]; + // if (dist < 48) { + // if (dir[2] > 0) dir[2] = 1; + // else dir[2] = -1; + // } else { + dir[2] = 0; + // } + if(dist > 50) + { + dist = 50; + } + speed = 400 - (300 - 6 * 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 result; + + BotClearMoveResult(&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 result; + + BotClearMoveResult(&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) + { +#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.failure = qtrue; + 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 result; + + BotClearMoveResult(&result); + // + MoverBottomCenter(reach, bottomcenter); + VectorSubtract(bottomcenter, ms->origin, bottomdir); + // + VectorSubtract(reach->end, ms->origin, topdir); + // + if(Q_fabs(bottomdir[2]) < Q_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_OriginOfEntityWithModelNum(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 result; + + BotClearMoveResult(&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_ELEVATORUP; + 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 result; + float dist, speed; + + BotClearMoveResult(&result); + // + 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) +{ + static int grapplemodelindex; + int i; + vec3_t dir; + aas_entityinfo_t entinfo; + + if (!grapplemodelindex) + { + grapplemodelindex = AAS_IndexFromModel("models/weapons/grapple/hook/tris.md2"); + } //end if + for (i = AAS_NextEntity(0); i; i = AAS_NextEntity(i)) + { + if (AAS_EntityModelindex(i) == grapplemodelindex) + { + AAS_EntityInfo(i, &entinfo); + if (VectorCompare(entinfo.origin, entinfo.old_origin)) + { + VectorSubtract(entinfo.origin, reach->end, dir); + //if hooked near the reachability end + if (VectorLength(dir) < 32) return 2; + } //end if + else + { + //still shooting hook + return 1; + } //end else + } //end if + } //end if + //no valid grapple at all + return 0; +} //end of the function GrappleState*/ +//=========================================================================== +// 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) +{ + static int grapplemodelindex; + static libvar_t *laserhook; + int i; + vec3_t dir; + aas_entityinfo_t entinfo; + + if(!laserhook) + { + laserhook = LibVar("laserhook", "0"); + } + if(!laserhook->value && !grapplemodelindex) + { + grapplemodelindex = AAS_IndexFromModel("models/weapons/grapple/hook/tris.md2"); + } //end if + for(i = AAS_NextEntity(0); i; i = AAS_NextEntity(i)) + { + if((!laserhook->value && AAS_EntityModelindex(i) == grapplemodelindex) +// || (laserhook->value && (AAS_EntityRenderFX(i) & RF_BEAM)) + ) + { + AAS_EntityInfo(i, &entinfo); + //if the origin is equal to the last visible origin + if(VectorCompare(entinfo.origin, entinfo.lastvisorigin)) + { + VectorSubtract(entinfo.origin, reach->end, dir); + //if hooked near the reachability end + if(VectorLength(dir) < 32) + { + return 2; + } + } //end if + else + { + //still shooting hook + return 1; + } //end else + } //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 != TRAVEL_GRAPPLEHOOK) + { + if((ms->moveflags & MFL_ACTIVEGRAPPLE) || ms->grapplevisible_time) + { + EA_Command(ms->client, CMD_HOOKOFF); + 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 result; + float dist, speed; + vec3_t dir, viewdir, org; + int state, areanum; + +#ifdef DEBUG_GRAPPLE + static int debugline; + + if(!debugline) + { + debugline = botimport.DebugLineCreate(); + } + botimport.DebugLineShow(debugline, reach->start, reach->end, LINECOLOR_BLUE); +#endif //DEBUG_GRAPPLE + + BotClearMoveResult(&result); + // + if(ms->moveflags & MFL_GRAPPLERESET) + { + EA_Command(ms->client, CMD_HOOKOFF); + ms->moveflags &= ~MFL_ACTIVEGRAPPLE; + return result; + } //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) + { + EA_Command(ms->client, CMD_HOOKOFF); + ms->moveflags &= ~MFL_ACTIVEGRAPPLE; + ms->moveflags |= MFL_GRAPPLERESET; + ms->reachability_time = 0; //end the reachability +#ifdef DEBUG_GRAPPLE + botimport.Print(PRT_ERROR, "grapple normal end\n"); +#endif //DEBUG_GRAPPLE + } //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 + EA_Command(ms->client, CMD_HOOKOFF); + ms->moveflags &= ~MFL_ACTIVEGRAPPLE; + ms->moveflags |= MFL_GRAPPLERESET; + ms->reachability_time = 0; //end the reachability + //result.failure = qtrue; + //result.type = RESULTTYPE_INVISIBLEGRAPPLE; + return result; + } //end if + } //end if + else + { + ms->grapplevisible_time = AAS_Time(); + } //end else + //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 && + Q_fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 2 && + Q_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 + EA_Command(ms->client, CMD_HOOKON); + 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 result; + + //botimport.Print(PRT_MESSAGE, "BotTravel_RocketJump: bah\n"); + BotClearMoveResult(&result); + // + 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) + { +// 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 + // +/* + vec3_t hordir, dir1, dir2, start, end, runstart; + float dist1, dist2, speed; + bot_moveresult_t result; + + botimport.Print(PRT_MESSAGE, "BotTravel_RocketJump: bah\n"); + BotClearMoveResult(&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_Attack(ms->client); + EA_Move(ms->client, hordir, 800); + // + 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 + */ + //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, WEAPONINDEX_ROCKET_LAUNCHER); + //weapon is used for movement + result.weapon = WEAPONINDEX_ROCKET_LAUNCHER; + 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) +{ + bot_moveresult_t result; + + BotClearMoveResult(&result); + // + 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; + bot_moveresult_t result; + + BotClearMoveResult(&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); + // + 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 result; + + BotClearMoveResult(&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 result; + + BotClearMoveResult(&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) + { + 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 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 + BotClearMoveResult(&result); + //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 || (goal->flags & GFL_NOSLOWAPPROACH)) + { + 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); + ms->lasttime = AAS_Time(); + // + return result; +} //end of the function BotMoveInGoalArea + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaRouteToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags, int *traveltime, + int *reachnum); +extern float VectorDistance(vec3_t v1, vec3_t v2); +void BotMoveToGoal(bot_moveresult_t * result, int movestate, bot_goal_t * goal, int travelflags) +{ + int reachnum = 0; // TTimo (might be used uninitialized in this function) + int lastreachnum, foundjumppad, ent; + aas_reachability_t reach, lastreach; + bot_movestate_t *ms; + + //vec3_t mins, maxs, up = {0, 0, 1}; + //bsp_trace_t trace; + //static int debugline; + + // + BotClearMoveResult(result); + // + 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 != 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 != 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 + /* Ridah, disabled this, or standing on little fragments causes problems + 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->areanum)) + { + 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 != TRAVEL_ELEVATOR)); + if(!ms->areanum) + { + result->failure = qtrue; + return; + } + //ms->areanum = BotFuzzyPointReachabilityArea(ms->origin); + //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 == 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 == TRAVEL_ELEVATOR || reach.traveltype == 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 + { + if(ms->reachability_time < AAS_Time()) + { + // the reachability timed out, add it to the ignore list +#ifdef AVOIDREACH + BotAddToAvoidReach(ms, reachnum, AVOIDREACH_TIME + 4); +#endif //AVOIDREACH + } +#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); + 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 || + //@TODO. The hardcoded distance here should actually be tied to speed. As it was, 20 was too big for + // slow moving walking Nazis. +// ((ms->lasttime > (AAS_Time()-0.5)) && (VectorDistance(ms->origin, ms->lastorigin) < 20*(AAS_Time()-ms->lasttime)))) + ((ms->lasttime > (AAS_Time() - 0.5)) && + (VectorDistance(ms->origin, ms->lastorigin) < 5 * (AAS_Time() - ms->lasttime)))) + { + reachnum = 0; + //botimport.Print(PRT_MESSAGE, "area change or timeout\n"); + } //end else if + } //end else + } //end if + //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->entitynum, + ms->lastgoalareanum, ms->lastareanum, + ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries, + goal, travelflags, travelflags); + //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() + (float)(0.01 * (AAS_AreaTravelTime(ms->areanum, ms->origin, reach.start) * 2) + + BotReachabilityTime(&reach) + 100); + if(reach.traveltype == TRAVEL_LADDER) + { + ms->reachability_time += 4.0; // allow more time to navigate ladders + } + // +#ifdef AVOIDREACH + if(reach.traveltype != TRAVEL_LADDER) + { // don't avoid ladders unless we were unable to reach them in time + BotAddToAvoidReach(ms, reachnum, 3); // add a short avoid reach time + } +#endif //AVOIDREACH + } //end if +#ifdef DEBUG + + else if(bot_developer) + { + botimport.Print(PRT_MESSAGE, "goal not reachable\n"); + 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; + // + if(goal->flags & GFL_DEBUGPATH) + { + AAS_ClearShownPolygons(); + AAS_ClearShownDebugLines(); + AAS_PrintTravelType(reach.traveltype); + // src area + AAS_ShowAreaPolygons(ms->areanum, 1, qtrue); + // dest area + AAS_ShowAreaPolygons(goal->areanum, 3, qtrue); + // reachability + AAS_ShowReachability(&reach); + AAS_ShowAreaPolygons(reach.areanum, 2, qtrue); + } + // +#ifdef DEBUG + //botimport.Print(PRT_MESSAGE, "client %d: ", ms->client); + //AAS_PrintTravelType(reach.traveltype); + //botimport.Print(PRT_MESSAGE, "\n"); +#endif //DEBUG + switch (reach.traveltype) + { + 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: + 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); + break; + } //end case + } //end switch + } //end if + else + { + result->failure = qtrue; + 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); + 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->entitynum, + ms->lastgoalareanum, ms->lastareanum, + ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries, + goal, travelflags, TFL_JUMPPAD); + 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 == 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); + //botimport.Print(PRT_MESSAGE, "\n"); +#endif //DEBUG + // + switch (reach.traveltype) + { + 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: + *result = BotFinishTravel_WeaponJump(ms, &reach); + break; + //case TRAVEL_BFGJUMP: + 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); + break; + } //end case + } //end switch +#ifdef DEBUG + if(bot_developer) + { + if(result->failure) + { + botimport.Print(PRT_MESSAGE, "client %d: movement failure in finish ", ms->client); + AAS_PrintTravelType(reach.traveltype); + 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); + ms->lasttime = AAS_Time(); +/* + // RF, try to look in the direction we will be moving ahead of time + if (reachnum > 0 && !(result->flags & (MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW))) { + vec3_t dir; + int ftraveltime, freachnum; + + AAS_ReachabilityFromNum( reachnum, &reach); + if (reach.areanum != goal->areanum) { + if (AAS_AreaRouteToGoalArea( reach.areanum, reach.end, goal->areanum, travelflags, &ftraveltime, &freachnum )) { + AAS_ReachabilityFromNum( freachnum, &reach); + VectorSubtract( reach.end, ms->origin, dir ); + VectorNormalize( dir ); + vectoangles( dir, result->ideal_viewangles ); + result->flags |= MOVERESULT_FUTUREVIEW; + } + } else { + VectorSubtract( goal->origin, ms->origin, dir ); + VectorNormalize( dir ); + vectoangles( dir, result->ideal_viewangles ); + result->flags |= MOVERESULT_FUTUREVIEW; + } + } +*/ + //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; + } + memset(ms->avoidreach, 0, MAX_AVOIDREACH * sizeof(int)); + memset(ms->avoidreachtimes, 0, MAX_AVOIDREACH * sizeof(float)); + memset(ms->avoidreachtries, 0, MAX_AVOIDREACH * sizeof(int)); + + // RF, also clear movestate stuff + ms->lastareanum = 0; + ms->lastgoalareanum = 0; + ms->lastreachnum = 0; +} //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; + } + memset(ms, 0, sizeof(bot_movestate_t)); +} //end of the function BotResetMoveState + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotSetupMoveAI(void) +{ + BotSetBrushModelTypes(); + sv_maxstep = LibVarValue("sv_step", "18"); + sv_maxbarrier = LibVarValue("sv_maxbarrier", "32"); + sv_gravity = LibVarValue("sv_gravity", "800"); + 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/src/engine/botlib/be_ai_move.h b/src/engine/botlib/be_ai_move.h new file mode 100644 index 0000000000..71dfde9731 --- /dev/null +++ b/src/engine/botlib/be_ai_move.h @@ -0,0 +1,155 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_ai_move.h + * + * desc: movement AI + * + * + *****************************************************************************/ + +//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_ACTIVEGRAPPLE 64 //bot is using the grapple hook +#define MFL_GRAPPLERESET 128 //bot has reset the grapple +#define MFL_WALK 256 //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_FUTUREVIEW 256 // RF, if we want to look ahead of time, this is a good direction +#define MOVERESULT_DIRECTMOVE 512 // direct movement + +// +#define MAX_AVOIDREACH 1 +// +#define RESULTTYPE_ELEVATORUP 1 +#define RESULTTYPE_INVISIBLEGRAPPLE 2 + +//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 + int areanum; +} 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; + +//resets the whole movestate +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 +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 +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 +void BotInitMoveState(int handle, bot_initmove_t * initmove); + +//must be called every map change +void BotSetBrushModelTypes(void); + +//setup movement AI +int BotSetupMoveAI(void); + +//shutdown movement AI +void BotShutdownMoveAI(void); + +// Ridah +//initialize avoid reachabilities +void BotInitAvoidReach(int handle); + +// done. diff --git a/src/engine/botlib/be_ai_weap.c b/src/engine/botlib/be_ai_weap.c new file mode 100644 index 0000000000..2bcdd2ee16 --- /dev/null +++ b/src/engine/botlib/be_ai_weap.c @@ -0,0 +1,658 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_ai_weap.c + * + * desc: weapon AI + * + * + *****************************************************************************/ + +#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 +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 +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} +}; + +structdef_t weaponinfo_struct = { + sizeof(weaponinfo_t), weaponinfo_fields +}; +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; + +bot_weaponstate_t *botweaponstates[MAX_CLIENTS + 1]; +weaponconfig_t *weaponconfig; + +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +extern qboolean g_singleplayer; + +int BotValidWeaponNumber(int weaponnum) +{ + // Thanks Arnout! TDF + // Gordon: 0 is valid in mp now too, for mr. pow + if((g_singleplayer && weaponnum < 0) || (!g_singleplayer && 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", "64"); + if(max_weaponinfo < 0) + { + botimport.Print(PRT_ERROR, "max_weaponinfo = %d\n", max_weaponinfo); + max_weaponinfo = 64; + LibVarSet("max_weaponinfo", "64"); + } //end if + max_projectileinfo = (int)LibVarValue("max_projectileinfo", "64"); + if(max_projectileinfo < 0) + { + botimport.Print(PRT_ERROR, "max_projectileinfo = %d\n", max_projectileinfo); + max_projectileinfo = 64; + LibVarSet("max_projectileinfo", "64"); + } //end if + strncpy(path, filename, MAX_PATH); + 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")) + { + 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 + 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 + 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)) + { + 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); + // + PS_SetBaseFolder("botfiles"); + ws->weaponweightconfig = ReadWeightConfig(filename); + PS_SetBaseFolder(""); + 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; + } + 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; + + //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; + + PS_SetBaseFolder("botfiles"); + file = LibVarString("weaponconfig", "weapons.c"); + weaponconfig = LoadWeaponConfig(file); + PS_SetBaseFolder(""); + 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/src/engine/botlib/be_ai_weap.h b/src/engine/botlib/be_ai_weap.h new file mode 100644 index 0000000000..0d4b48af6d --- /dev/null +++ b/src/engine/botlib/be_ai_weap.h @@ -0,0 +1,122 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_ai_weap.h + * + * desc: weapon AI + * + * + *****************************************************************************/ + +//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/src/engine/botlib/be_ai_weight.c b/src/engine/botlib/be_ai_weight.c new file mode 100644 index 0000000000..0529bd5f02 --- /dev/null +++ b/src/engine/botlib/be_ai_weight.c @@ -0,0 +1,1230 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_weight.c + * + * desc: fuzzy logic + * + * + *****************************************************************************/ + +#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 + + 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 +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); + 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 + scale = (inventory[fs->index] - fs->value) / (fs->next->value - fs->value); + //scale between the two weights + return scale * w1 + (1 - 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 + scale = (inventory[fs->index] - fs->value) / (fs->next->value - fs->value); + //scale between the two weights + return scale * w1 + (1 - 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 = (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/src/engine/botlib/be_ai_weight.h b/src/engine/botlib/be_ai_weight.h new file mode 100644 index 0000000000..11bae907d7 --- /dev/null +++ b/src/engine/botlib/be_ai_weight.h @@ -0,0 +1,104 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_ai_weight.h + * + * desc: fuzzy weights + * + * + *****************************************************************************/ + +#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/src/engine/botlib/be_ea.c b/src/engine/botlib/be_ea.c new file mode 100644 index 0000000000..fa0326c9d5 --- /dev/null +++ b/src/engine/botlib/be_ea.c @@ -0,0 +1,569 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_ea.c + * + * desc: elementary actions + * + * + *****************************************************************************/ + +#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_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_Reload(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_RELOAD; +} //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_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_Prone(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_PRONE; +} //end of the function EA_Prone + +//=========================================================================== +// +// 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; + 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 * init) +{ + 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; + } + + if(init) + { + memcpy(bi, init, sizeof(bot_input_t)); + } +} //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/src/engine/botlib/be_ea.h b/src/engine/botlib/be_ea.h new file mode 100644 index 0000000000..9f5c464ea1 --- /dev/null +++ b/src/engine/botlib/be_ea.h @@ -0,0 +1,82 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_ea.h + * + * desc: elementary actions + * + * + *****************************************************************************/ + +//ClientCommand elementary actions +void EA_Say(int client, char *str); +void EA_SayTeam(int client, char *str); +void EA_UseItem(int client, char *it); +void EA_DropItem(int client, char *it); +void EA_UseInv(int client, char *inv); +void EA_DropInv(int client, char *inv); +void EA_Command(int client, char *command); + +//regular elementary actions +void EA_SelectWeapon(int client, int weapon); +void EA_Attack(int client); +void EA_Reload(int client); +void EA_Respawn(int client); +void EA_Talk(int client); +void EA_Gesture(int client); +void EA_Use(int client); +void EA_Jump(int client); +void EA_DelayedJump(int client); +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_Move(int client, vec3_t dir, float speed); +void EA_View(int client, vec3_t viewangles); +void EA_Prone(int client); + +//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, bot_input_t * init); + +//setup and shutdown routines +int EA_Setup(void); +void EA_Shutdown(void); diff --git a/src/engine/botlib/be_interface.c b/src/engine/botlib/be_interface.c new file mode 100644 index 0000000000..d2740fcc12 --- /dev/null +++ b/src/engine/botlib/be_interface.c @@ -0,0 +1,1023 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_interface.c + * + * desc: bot library interface + * + * + *****************************************************************************/ + +#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: - +//=========================================================================== +// Ridah, faster Win32 code +#ifdef _WIN32 +#undef MAX_PATH // this is an ugly hack, to temporarily ignore the current definition, since it's also defined in windows.h +#include +#undef MAX_PATH +#define MAX_PATH MAX_QPATH +#endif + +int Sys_MilliSeconds(void) +{ +// Ridah, faster Win32 code +#if 0 //def _WIN32 + int sys_curtime; + static qboolean initialized = qfalse; + static int sys_timeBase; + + if(!initialized) + { + sys_timeBase = timeGetTime(); + initialized = qtrue; + } + sys_curtime = timeGetTime() - sys_timeBase; + + return sys_curtime; +#else + return clock() * 1000 / CLOCKS_PER_SEC; +#endif +} //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) +{ +// return qtrue; + + 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: - +//=========================================================================== +extern define_t *globaldefines; +int Export_BotLibSetup(qboolean singleplayer) +{ + int errnum; + + bot_developer = LibVarGetValue("bot_developer"); + //initialize byte swapping (litte endian etc.) + Log_Open("botlib.log"); + // + 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; + } +// START Arnout changes, 28-08-2002. +// added single player + errnum = BotSetupGoalAI(singleplayer); //be_ai_goal.c +// END Arnout changes, 28-08-2002. + 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; + } + + globaldefines = NULL; + + botlibsetup = qtrue; + botlibglobals.botlibsetup = qtrue; + + return BLERR_NOERROR; +} //end of the function Export_BotLibSetup + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Export_BotLibShutdown(void) +{ + static int recursive = 0; + + if(!BotLibSetup("BotLibShutdown")) + { + return BLERR_LIBRARYNOTSETUP; + } + // + if(recursive) + { + return BLERR_NOERROR; + } + recursive = 1; + // shutdown all AI subsystems + 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 + // shutdown AAS + AAS_Shutdown(); + // shutdown bot elemantary actions + EA_Shutdown(); + // free all libvars + LibVarDeAllocAll(); + // remove all global defines from the pre compiler + PC_RemoveAllGlobalDefines(); + // shut down library log file + Log_Shutdown(); + // + botlibsetup = qfalse; + botlibglobals.botlibsetup = qfalse; + recursive = 0; + // print any files still open + PC_CheckOpenSourceHandles(); + // +#ifdef _DEBUG + Log_AlwaysOpen("memory.log"); + PrintMemoryLabels(); + Log_Shutdown(); +#endif + 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: - +//=========================================================================== +void AAS_EnableAllAreas(void); + +int Export_BotLibLoadMap(const char *mapname) +{ +#ifdef DEBUG + int starttime = Sys_MilliSeconds(); +#endif + int errnum; + + if(!BotLibSetup("BotLoadMap")) + { + return BLERR_LIBRARYNOTSETUP; + } + // + // if the mapname is NULL, then this is a restart + if(!mapname) + { + // START Arnout changes, 29-08-2002. + // don't init the heap if no aas loaded, causes "SV_Bot_HunkAlloc: Alloc with marks already set" + if((*aasworld).loaded) + { + AAS_InitAASLinkHeap(); + AAS_EnableAllAreas(); + } + // END Arnout changes, 29-08-2002. + (*aasworld).numframes = 0; + memset((*aasworld).arealinkedentities, 0, (*aasworld).numareas * sizeof(aas_link_t *)); + memset((*aasworld).entities, 0, (*aasworld).maxentities * sizeof(aas_entity_t)); + return BLERR_NOERROR; + } + // + 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 entnum, + int lastgoalareanum, int lastareanum, + int *avoidreach, float *avoidreachtimes, int *avoidreachtries, + bot_goal_t * goal, int travelflags, int movetravelflags); + +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); + +int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, vec3_t enemyorigin, int enemyareanum, + int travelflags, float maxdist, vec3_t distpos); + +int AAS_FindAttackSpotWithinRange(int srcnum, int rangenum, int enemynum, float rangedist, int travelflags, + float *outpos); + +int AAS_ListAreasInRange(vec3_t srcpos, int srcarea, float range, int travelflags, vec3_t * outareas, int maxareas); + +int AAS_AvoidDangerArea(vec3_t srcpos, int srcarea, vec3_t dangerpos, int dangerarea, float range, int travelflags); + +int AAS_Retreat(int *dangerSpots, int dangerSpotCount, vec3_t srcpos, int srcarea, vec3_t dangerpos, int dangerarea, + float range, float dangerRange, int travelflags); + +int AAS_AlternativeRouteGoals(vec3_t start, vec3_t goal, int travelflags, + aas_altroutegoal_t * altroutegoals, int maxaltroutegoals, int color); + +void AAS_SetAASBlockingEntity(vec3_t absmin, vec3_t absmax, int blocking); + +void AAS_RecordTeamDeathArea(vec3_t srcpos, int srcarea, int team, int teamCount, int travelflags); + +int BotExportTest(int parm0, char *parm1, vec3_t parm2, vec3_t parm3) +{ + static int area = -1; + static int line[2]; + int newarea, i, highlightarea, bot_testhidepos, hideposarea, bot_debug; + vec3_t forward, origin; + +// vec3_t mins = {-16, -16, -24}; +// vec3_t maxs = {16, 16, 32}; + + if(!aasworld->loaded) + { + return 0; + } + + AAS_SetCurrentWorld(0); + + for(i = 0; i < 2; i++) + { + if(!line[i]) + { + line[i] = botimport.DebugLineCreate(); + } + } + +// AAS_ClearShownDebugLines(); + bot_testhidepos = LibVarGetValue("bot_testhidepos"); + if(bot_testhidepos) + { + VectorCopy(parm2, origin); + newarea = BotFuzzyPointReachabilityArea(origin); + + if(parm0 & 1) + { + botlibglobals.goalareanum = newarea; + VectorCopy(origin, botlibglobals.goalorigin); + botimport.Print(PRT_MESSAGE, "new enemy position %2.1f %2.1f %2.1f area %d\n", origin[0], origin[1], origin[2], + newarea); + } //end if + + AAS_ClearShownPolygons(); + AAS_ClearShownDebugLines(); + hideposarea = AAS_NearestHideArea(0, origin, AAS_PointAreaNum(origin), 0, + botlibglobals.goalorigin, botlibglobals.goalareanum, TFL_DEFAULT, 99999, NULL); + + //area we are currently in + AAS_ShowAreaPolygons(newarea, 1, qtrue); + + //enemy position + AAS_ShowAreaPolygons(botlibglobals.goalareanum, 2, qtrue); + + //area we should go hide + AAS_ShowAreaPolygons(hideposarea, 4, qtrue); + + return 0; + } + + highlightarea = LibVarGetValue("bot_highlightarea"); + if(highlightarea > 0) + { + newarea = highlightarea; + } + else + { + VectorCopy(parm2, origin); + + //origin[2] += 0.5; + newarea = BotFuzzyPointReachabilityArea(origin); + } //end else + + bot_debug = LibVarGetValue("bot_debug"); + if(bot_debug == 9) + { + aas_clientmove_t move; + vec3_t dest; + qboolean this_success; + + if(parm0 & 1) + { + 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); + } + + VectorCopy(parm2, origin); + VectorCopy(botlibglobals.goalorigin, dest); + + // debug direct movement + VectorSubtract(dest, origin, forward); + VectorNormalize(forward); + VectorScale(forward, 300, forward); + + this_success = AAS_PredictClientMovement(&move, 0, origin, + -1, qfalse, + forward, dest, -1, + 40, 0.05, + SE_ENTERAREA | SE_HITGROUNDDAMAGE | SE_HITENT | SE_HITGROUNDAREA | SE_STUCK | + SE_GAP, botlibglobals.goalareanum, qtrue); + + if(this_success) + { + switch (move.stopevent) + { + case SE_ENTERAREA: + case SE_HITENT: + case SE_HITGROUNDAREA: + break; + default: + this_success = qfalse; + } + } + + if(this_success != botlibglobals.lastsuccess) + { + botimport.Print(PRT_MESSAGE, "DirectMove: %s\n", this_success ? "SUCCESS" : "FAILURE"); + botlibglobals.lastsuccess = this_success; + } + + return 0; + } + + botimport.Print(PRT_MESSAGE, "\rtravel time to goal (%d) = %d ", botlibglobals.goalareanum, + AAS_AreaTravelTimeToGoalArea(newarea, origin, botlibglobals.goalareanum, TFL_DEFAULT)); + 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)); + + if(aasworld->areasettings[area].areaflags & AREA_LIQUID) + { + botimport.Print(PRT_MESSAGE, "liquid area\n"); + } //end if + + botimport.Print(PRT_MESSAGE, "area contents: "); + if(aasworld->areasettings[area].contents & AREACONTENTS_MOVER) + { + botimport.Print(PRT_MESSAGE, "mover "); + } //end if + 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, "slag "); + } //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_DONOTENTER) + { + botimport.Print(PRT_MESSAGE, "do not enter "); + } //end if + if(aasworld->areasettings[area].contents & AREACONTENTS_DONOTENTER_LARGE) + { + botimport.Print(PRT_MESSAGE, "do not enter large "); + } //end if + if(!aasworld->areasettings[area].contents) + { + botimport.Print(PRT_MESSAGE, "empty "); + } //end if + + botimport.Print(PRT_MESSAGE, "\n"); + botimport.Print(PRT_MESSAGE, "area flags: "); + + if(aasworld->areasettings[area].areaflags & AREA_LADDER) + { + botimport.Print(PRT_MESSAGE, "ladder "); + } + if(aasworld->areasettings[area].areaflags & AREA_GROUNDED) + { + botimport.Print(PRT_MESSAGE, "grounded "); + } + if(aasworld->areasettings[area].areaflags & AREA_LIQUID) + { + botimport.Print(PRT_MESSAGE, "liquid "); + } + if(aasworld->areasettings[area].areaflags & AREA_DISABLED) + { + botimport.Print(PRT_MESSAGE, "DISABLED "); + } + if(aasworld->areasettings[area].areaflags & AREA_AVOID) + { + botimport.Print(PRT_MESSAGE, "AVOID "); + } + + 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)); + } + + if(parm0 & 1) + { + 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); + } + + AAS_ClearShownPolygons(); + AAS_ClearShownDebugLines(); + + if(parm0 & 8) + { + int jk = 0; + + if(parm0 & 16) + { + for(; jk < aasworld->numareas; jk++) + { + if(!(aasworld->areasettings[jk].areaflags & AREA_DISABLED)) + { + AAS_ShowAreaPolygons(jk, 1, parm0 & 4); + } + } + } + else + { + for(; jk < aasworld->numareas; jk++) + { + AAS_ShowAreaPolygons(jk, 1, parm0 & 4); + } + } + } + else + { + 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; + bot_goal_t goal; + aas_reachability_t reach; + static int lastreach; + + goal.areanum = botlibglobals.goalareanum; + VectorCopy(botlibglobals.goalorigin, goal.origin); + reachnum = BotGetReachabilityToGoal(origin, newarea, -1, + lastgoalareanum, lastareanum, + avoidreach, avoidreachtimes, avoidreachtries, + &goal, TFL_DEFAULT | TFL_FUNCBOB, TFL_DEFAULT); + AAS_ReachabilityFromNum(reachnum, &reach); + if(lastreach != reachnum) + { + botimport.Print(PRT_MESSAGE, "Travel Type: "); + AAS_PrintTravelType(reach.traveltype); + botimport.Print(PRT_MESSAGE, "\n"); + } + lastreach = reachnum; + AAS_ShowReachability(&reach); + } //end else + VectorClear(forward); + + 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_TraceAreas = AAS_TraceAreas; + aas->AAS_BBoxAreas = AAS_BBoxAreas; + aas->AAS_AreaCenter = AAS_AreaCenter; + aas->AAS_AreaWaypoint = AAS_AreaWaypoint; + //-------------------------------------------- + // 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; + aas->AAS_AreaLadder = AAS_AreaLadder; + //-------------------------------------------- + // be_aas_route.c + //-------------------------------------------- + aas->AAS_AreaTravelTimeToGoalArea = AAS_AreaTravelTimeToGoalArea; + //-------------------------------------------- + // be_aas_move.c + //-------------------------------------------- + aas->AAS_Swimming = AAS_Swimming; + aas->AAS_PredictClientMovement = AAS_PredictClientMovement; + + // Ridah, route-tables + //-------------------------------------------- + // be_aas_routetable.c + //-------------------------------------------- + aas->AAS_RT_ShowRoute = AAS_RT_ShowRoute; + aas->AAS_RT_GetHidePos = AAS_RT_GetHidePos; + aas->AAS_FindAttackSpotWithinRange = AAS_FindAttackSpotWithinRange; + aas->AAS_NearestHideArea = AAS_NearestHideArea; + aas->AAS_ListAreasInRange = AAS_ListAreasInRange; + aas->AAS_AvoidDangerArea = AAS_AvoidDangerArea; + aas->AAS_Retreat = AAS_Retreat; + aas->AAS_AlternativeRouteGoals = AAS_AlternativeRouteGoals; + aas->AAS_SetAASBlockingEntity = AAS_SetAASBlockingEntity; + aas->AAS_RecordTeamDeathArea = AAS_RecordTeamDeathArea; + // done. + + // Ridah, multiple AAS files + aas->AAS_SetCurrentWorld = AAS_SetCurrentWorld; + // done. + +} + + +/* +============ +Init_EA_Export +============ +*/ +static void Init_EA_Export(ea_export_t * ea) +{ + //ClientCommand elementary actions + ea->EA_Say = EA_Say; + ea->EA_SayTeam = EA_SayTeam; + ea->EA_UseItem = EA_UseItem; + ea->EA_DropItem = EA_DropItem; + ea->EA_UseInv = EA_UseInv; + ea->EA_DropInv = EA_DropInv; + ea->EA_Gesture = EA_Gesture; + ea->EA_Command = EA_Command; + ea->EA_SelectWeapon = EA_SelectWeapon; + ea->EA_Talk = EA_Talk; + ea->EA_Attack = EA_Attack; + ea->EA_Reload = EA_Reload; + ea->EA_Use = EA_Use; + ea->EA_Respawn = EA_Respawn; + ea->EA_Jump = EA_Jump; + ea->EA_DelayedJump = EA_DelayedJump; + ea->EA_Crouch = EA_Crouch; + ea->EA_Walk = EA_Walk; + 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_Move = EA_Move; + ea->EA_View = EA_View; + ea->EA_GetInput = EA_GetInput; + ea->EA_EndRegular = EA_EndRegular; + ea->EA_ResetInput = EA_ResetInput; + ea->EA_Prone = EA_Prone; +} + + +/* +============ +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->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; + // Ridah + ai->BotInitAvoidReach = BotInitAvoidReach; + // done. + //----------------------------------- + // 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) +{ +#ifndef BOTLIB_STATIC + assert(import); +#endif + botimport = *import; +#ifndef BOTLIB_STATIC + assert(botimport.Print); +#endif + 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_RemoveAllGlobalDefines = PC_RemoveAllGlobalDefines; + 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.PC_UnreadLastTokenHandle = PC_UnreadLastTokenHandle; + + 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; +} +#ifndef BOTLIB_STATIC +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); + + botimport.Print(PRINT_ALL, "%s", text); +} + +void QDECL Com_DPrintf(const char *msg, ...) +{ + va_list argptr; + char text[1024]; + + va_start(argptr, msg); + Q_vsnprintf(text, sizeof(text), msg, argptr); + va_end(argptr); + + botimport.Print(PRINT_DEVELOPER, "%s", 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); + + botimport.Error(level, "%s", text); +} +#endif \ No newline at end of file diff --git a/src/engine/botlib/be_interface.h b/src/engine/botlib/be_interface.h new file mode 100644 index 0000000000..683e5f628c --- /dev/null +++ b/src/engine/botlib/be_interface.h @@ -0,0 +1,99 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: be_interface.h + * + * desc: botlib interface + * + * + *****************************************************************************/ + +/* +"Do not go where the path leads, rather go where there's no track and leave a trail." + +"AAS (Area Awareness System)" + +"Part of the Gladiator is BoGuS (Bot Guidance System)" + +"ANSI (Advanced Navigational System Interface)" + +"to make things work the only thing you really have to do is think things work." + +"a madman is just someone living in another reality which isn't shared among many people" +*/ + +//#define DEBUG //debug code +#define RANDOMIZE //randomize bot behaviour +#if defined( WIN32 ) || defined( _WIN32 ) +#define AASZIP //allow reading directly from aasX.zip files +#endif +#define QUAKE2 //bot for Quake2 +//#define HALFLIFE //bot for Half-Life + +//========================================================== +// +// global variable structures +// +//========================================================== + +//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; + qboolean lastsuccess; +//#endif +} botlib_globals_t; + +//========================================================== +// +// global variables +// +//========================================================== + +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/src/engine/botlib/botai.h b/src/engine/botlib/botai.h new file mode 100644 index 0000000000..e16f07c6fe --- /dev/null +++ b/src/engine/botlib/botai.h @@ -0,0 +1,116 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +//=========================================================================== +// +// Name: botai.h +// Function: bot AI +// Programmer: Mr Elusive (MrElusive@idsoftware.com) +// Last update: 1999-08-18 +// Tab Size: 3 +//=========================================================================== + +//debug line colors +#define LINECOLOR_NONE -1 +#define LINECOLOR_RED 1 +#define LINECOLOR_GREEN 2 +#define LINECOLOR_BLUE 3 +#define LINECOLOR_YELLOW 4 +#define LINECOLOR_ORANGE 5 + +//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 + +//some maxs +#define MAX_NETNAME 36 +#define MAX_CLIENTSKINNAME 128 +#define MAX_FILEPATH 144 +#define MAX_CHARACTERNAME 144 + +#ifndef 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; + +#define BSPTRACE +#endif // BSPTRACE + +// +// imported functions used for the BotAI +// + + +// from the server +/* +void trap_Cvar_Register( vmCvar_t *cvar, const char *var_name, const char *value, int flags ); +void trap_Cvar_Update( vmCvar_t *cvar ); +void trap_Cvar_Set( const char *var_name, const char *value ); +int trap_Cvar_VariableIntegerValue( const char *var_name ); +void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); +void trap_GetConfigstring( int num, char *buffer, int bufferSize ); +void trap_GetServerinfo( char *buffer, int bufferSize ); +int trap_PointContents( const vec3_t point, int passEntityNum ); +qboolean trap_InPVS( const vec3_t p1, const vec3_t p2 ); +int trap_BotAllocateClient( int clientNum ); +void trap_BotFreeClient( int clientNum ); +*/ diff --git a/src/engine/botlib/botlib.def b/src/engine/botlib/botlib.def new file mode 100644 index 0000000000..cebecced8e --- /dev/null +++ b/src/engine/botlib/botlib.def @@ -0,0 +1,2 @@ +EXPORTS + GetBotLibAPI \ No newline at end of file diff --git a/src/engine/botlib/botlib.h b/src/engine/botlib/botlib.h new file mode 100644 index 0000000000..f51abf2ff9 --- /dev/null +++ b/src/engine/botlib/botlib.h @@ -0,0 +1,616 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +//=========================================================================== +// +/***************************************************************************** + * name: botlib.h + * + * desc: bot AI library + * + * + *****************************************************************************/ + +#include "be_aas.h" + +#define BOTLIB_API_VERSION 2 + +struct aas_clientmove_s; +struct aas_entityinfo_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 MAX_DEBUGPOLYS 4096 + +typedef struct bot_debugpoly_s +{ + int inuse; + int color; + int numPoints; + vec3_t points[128]; +} bot_debugpoly_t; + +typedef void (*BotPolyFunc) (int color, int numPoints, float *points); + +// RF, these need to be here so the botlib also knows how many bot game entities there are +#define NUM_BOTGAMEENTITIES 384 + +#define BLOCKINGFLAG_MOVER ( ~0x7fffffff ) + +//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_LIBRARYALREADYSETUP 2 //BotSetupLibrary: library already setup +#define BLERR_INVALIDCLIENTNUMBER 3 //invalid client number +#define BLERR_INVALIDENTITYNUMBER 4 //invalid entity number +#define BLERR_NOAASFILE 5 //BotLoadMap: no AAS file available +#define BLERR_CANNOTOPENAASFILE 6 //BotLoadMap: cannot open AAS file +#define BLERR_CANNOTSEEKTOAASFILE 7 //BotLoadMap: cannot seek to AAS file +#define BLERR_CANNOTREADAASHEADER 8 //BotLoadMap: cannot read AAS header +#define BLERR_WRONGAASFILEID 9 //BotLoadMap: incorrect AAS file id +#define BLERR_WRONGAASFILEVERSION 10 //BotLoadMap: incorrect AAS file version +#define BLERR_CANNOTREADAASLUMP 11 //BotLoadMap: cannot read AAS file lump +#define BLERR_NOBSPFILE 12 //BotLoadMap: no BSP file available +#define BLERR_CANNOTOPENBSPFILE 13 //BotLoadMap: cannot open BSP file +#define BLERR_CANNOTSEEKTOBSPFILE 14 //BotLoadMap: cannot seek to BSP file +#define BLERR_CANNOTREADBSPHEADER 15 //BotLoadMap: cannot read BSP header +#define BLERR_WRONGBSPFILEID 16 //BotLoadMap: incorrect BSP file id +#define BLERR_WRONGBSPFILEVERSION 17 //BotLoadMap: incorrect BSP file version +#define BLERR_CANNOTREADBSPLUMP 18 //BotLoadMap: cannot read BSP file lump +#define BLERR_AICLIENTNOTSETUP 19 //BotAI: client not setup +#define BLERR_AICLIENTALREADYSETUP 20 //BotSetupClient: client already setup +#define BLERR_AIMOVEINACTIVECLIENT 21 //BotMoveClient: cannot move inactive client +#define BLERR_AIMOVETOACTIVECLIENT 22 //BotMoveClient: cannot move to active client +#define BLERR_AICLIENTALREADYSHUTDOWN 23 //BotShutdownClient: client not setup +#define BLERR_AIUPDATEINACTIVECLIENT 24 //BotUpdateClient: called for inactive client +#define BLERR_AICMFORINACTIVECLIENT 25 //BotConsoleMessage: called for inactive client +#define BLERR_SETTINGSINACTIVECLIENT 26 //BotClientSettings: called for inactive client +#define BLERR_CANNOTLOADICHAT 27 //BotSetupClient: cannot load initial chats +#define BLERR_CANNOTLOADITEMWEIGHTS 28 //BotSetupClient: cannot load item weights +#define BLERR_CANNOTLOADITEMCONFIG 29 //BotSetupLibrary: cannot load item config +#define BLERR_CANNOTLOADWEAPONWEIGHTS 30 //BotSetupClient: cannot load weapon weights +#define BLERR_CANNOTLOADWEAPONCONFIG 31 //BotSetupLibrary: cannot load weapon config +#define BLERR_INVALIDSOUNDINDEX 32 //BotAddSound: invalid sound index value + +//action flags +#define ACTION_ATTACK 1 +#define ACTION_USE 2 +#define ACTION_RESPAWN 4 +#define ACTION_JUMP 8 +#define ACTION_MOVEUP 8 +#define ACTION_CROUCH 16 +#define ACTION_MOVEDOWN 16 +#define ACTION_MOVEFORWARD 32 +#define ACTION_MOVEBACK 64 +#define ACTION_MOVELEFT 128 +#define ACTION_MOVERIGHT 256 +#define ACTION_DELAYEDJUMP 512 +#define ACTION_TALK 1024 +#define ACTION_GESTURE 2048 +#define ACTION_WALK 4096 +#define ACTION_RELOAD 8192 +// START xkan, 9/16/2002 +#define ACTION_PRONE 16384 +// END xkan, 9/16/2002 + +//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; + +//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 +// int weapAnim; // mask off ANIM_TOGGLEBIT //----(SA) added +//----(SA) didn't want to comment in as I wasn't sure of any implications of changing this structure. +} 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, ...); + + // abort the game + void (QDECL * Error) (int errorLevel, const 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); + void (*FreeMemory) (void *ptr); + void (*FreeZoneMemory) (void); + void *(*HunkAlloc) (int size); + //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); + bot_debugpoly_t *(*DebugPolygonGetFree) (void); + void (*DebugPolygonDelete) (int id); + void (*DebugPolygonDeletePointer) (bot_debugpoly_t * pPoly); + // + // Ridah, Cast AI stuff + qboolean(*BotVisibleFromPos) (vec3_t srcpos, int srcnum, vec3_t destpos, int destnum, qboolean updateVisPos); + qboolean(*BotCheckAttackAtPos) (int entnum, int enemy, vec3_t pos, qboolean ducking, qboolean allowHitWorld); + // done. + // Gordon: ability for botlib to check for singleplayer + // Arnout: removed again, botlibsetup already has a parameter 'singleplayer' + //qboolean (*BotGameIsSinglePlayer) ( void ); + + + // Gordon: direct hookup into rendering, stop using this silly debugpoly faff + void (*BotDrawPolygon) (int color, int numPoints, float *points); +} 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_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); + void (*AAS_AreaCenter) (int areanum, vec3_t center); + qboolean(*AAS_AreaWaypoint) (int areanum, vec3_t center); + //-------------------------------------------- + // 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); + int (*AAS_AreaLadder) (int areanum); + //-------------------------------------------- + // be_aas_route.c + //-------------------------------------------- + int (*AAS_AreaTravelTimeToGoalArea) (int areanum, vec3_t origin, int goalareanum, int travelflags); + //-------------------------------------------- + // 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); + + // Ridah, route-tables + //-------------------------------------------- + // be_aas_routetable.c + //-------------------------------------------- + void (*AAS_RT_ShowRoute) (vec3_t srcpos, int srcnum, int destnum); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + qboolean(*AAS_RT_GetHidePos) (vec3_t srcpos, int srcnum, int srcarea, vec3_t destpos, int destnum, int destarea, + vec3_t returnPos); + int (*AAS_FindAttackSpotWithinRange) (int srcnum, int rangenum, int enemynum, float rangedist, int travelflags, + float *outpos); + int (*AAS_ListAreasInRange) (vec3_t srcpos, int srcarea, float range, int travelflags, vec3_t * outareas, + int maxareas); + int (*AAS_AvoidDangerArea) (vec3_t srcpos, int srcarea, vec3_t dangerpos, int dangerarea, float range, + int travelflags); + int (*AAS_Retreat) (int *dangerSpots, int dangerSpotCount, vec3_t srcpos, int srcarea, vec3_t dangerpos, + int dangerarea, float range, float dangerRange, int travelflags); + int (*AAS_AlternativeRouteGoals) (vec3_t start, vec3_t goal, int travelflags, aas_altroutegoal_t * altroutegoals, + int maxaltroutegoals, int color); + void (*AAS_SetAASBlockingEntity) (vec3_t absmin, vec3_t absmax, int blocking); + int (*AAS_NearestHideArea) (int srcnum, vec3_t origin, int areanum, int enemynum, vec3_t enemyorigin, + int enemyareanum, int travelflags, float maxdist, vec3_t distpos); + void (*AAS_RecordTeamDeathArea) (vec3_t srcpos, int srcarea, int team, int teamCount, int travelflags); + // done. + + // Ridah + void (*AAS_SetCurrentWorld) (int index); + // done. + +} aas_export_t; + +typedef struct ea_export_s +{ + //ClientCommand elementary actions + void (*EA_Say) (int client, char *str); + void (*EA_SayTeam) (int client, char *str); + void (*EA_UseItem) (int client, char *it); + void (*EA_DropItem) (int client, char *it); + void (*EA_UseInv) (int client, char *inv); + void (*EA_DropInv) (int client, char *inv); + void (*EA_Gesture) (int client); + void (*EA_Command) (int client, char *command); + //regular elementary actions + void (*EA_SelectWeapon) (int client, int weapon); + void (*EA_Talk) (int client); + void (*EA_Attack) (int client); + void (*EA_Reload) (int client); + void (*EA_Use) (int client); + void (*EA_Respawn) (int client); + void (*EA_Jump) (int client); + void (*EA_DelayedJump) (int client); + 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_Move) (int client, vec3_t dir, float speed); + void (*EA_View) (int client, vec3_t viewangles); + void (*EA_Prone) (int client); + //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, bot_input_t * init); +} ea_export_t; + +typedef struct ai_export_s +{ + //----------------------------------- + // be_ai_char.h + //----------------------------------- + int (*BotLoadCharacter) (char *charfile, int 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); + //----------------------------------- + // 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 (*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); + // Ridah + void (*BotInitAvoidReach) (int handle); + // done. + //----------------------------------- + // 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) (qboolean singleplayer); + //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); + void (*PC_RemoveAllGlobalDefines) (void); + 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_UnreadLastTokenHandle) (int handle); + + //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 Quake2 base directory +"gamedir" "" l_utils.c Quake2 game directory +"cddir" "" l_utils.c Quake2 CD directory + +"autolaunchbspc" "0" be_aas_load.c automatically launch (Win)BSPC +"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 + +"sv_friction" "6" be_aas_move.c ground friction +"sv_stopspeed" "100" be_aas_move.c stop speed +"sv_gravity" "800" be_aas_move.c gravity value +"sv_waterfriction" "1" be_aas_move.c water friction +"sv_watergravity" "400" be_aas_move.c gravity in water +"sv_maxvelocity" "300" be_aas_move.c maximum velocity +"sv_maxwalkvelocity" "300" be_aas_move.c maximum walk velocity +"sv_maxcrouchvelocity" "100" be_aas_move.c maximum crouch velocity +"sv_maxswimvelocity" "150" be_aas_move.c maximum swim velocity +"sv_walkaccelerate" "10" be_aas_move.c walk acceleration +"sv_airaccelerate" "1" be_aas_move.c air acceleration +"sv_swimaccelerate" "4" be_aas_move.c swim acceleration +"sv_maxstep" "18" be_aas_move.c maximum step height +"sv_maxbarrier" "32" be_aas_move.c maximum barrier height +"sv_maxsteepness" "0.7" be_aas_move.c maximum floor steepness +"sv_jumpvel" "270" be_aas_move.c jump z velocity +"sv_maxwaterjump" "20" be_aas_move.c maximum waterjump height + +"max_aaslinks" "4096" be_aas_sample.c maximum links in the AAS +"max_bsplinks" "4096" be_aas_bsp.c maximum links in the BSP + +"notspawnflags" "2048" be_ai_goal.c entities with these spawnflags will be removed +"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 +"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 +"framereachability" "" be_aas_reach.c number of reachabilities to calucate per frame +"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 +"nooptimize" "0" be_aas_main.c no aas optimization + +"laserhook" "0" be_ai_move.c 0 = CTF hook, 1 = laser hook + +*/ diff --git a/src/engine/botlib/botlib.vcxproj b/src/engine/botlib/botlib.vcxproj new file mode 100644 index 0000000000..bfbb04ac28 --- /dev/null +++ b/src/engine/botlib/botlib.vcxproj @@ -0,0 +1,300 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {EFA65954-05A8-43C4-B4FE-FAFE8E678AB7} + + + + DynamicLibrary + false + MultiByte + + + DynamicLibrary + false + MultiByte + + + DynamicLibrary + false + MultiByte + + + DynamicLibrary + false + MultiByte + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + .\Release\x32\ + .\Release\x64\ + .\Release\x32\ + .\Release\x64\ + .\Debug\x32\ + .\Debug\x64\ + .\Debug\x32\ + .\Debug\x64\ + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + false + false + + + + OnlyExplicitInline + WIN32;NDEBUG;_LIB;BOTLIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + MultiThreadedDLL + false + true + + + .\Release\x32\botlib.pch + .\Release\x32\ + .\Release\x32\ + .\Release\x32\ + Level4 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + .\Release\x32\botlib.lib + true + + + ../../../bin/win32/release/botlib.dll + .\Release\x32\botlib.lib + .\botlib.def + + + + + OnlyExplicitInline + WIN32;WIN64;__WIN64__;NDEBUG;_LIB;BOTLIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + MultiThreaded + false + true + + + .\Release\x64\botlib.pch + .\Release\x64\ + .\Release\x64\ + .\Release\x64\ + Level4 + true + true + false + false + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + .\Release\x64\botlib.lib + true + MachineX64 + + + ../../../bin/win64/release/botlib.dll + .\botlib.def + .\Release\x64\botlib.lib + + + + + Disabled + WIN32;_DEBUG;_LIB;BOTLIB;DEBUG;_CRT_SECURE_NO_WARNINGS;USE_INCREASED_ENTITIES;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + .\Debug\x32\botlib.pch + .\Debug\x32/ + .\Debug\x32/ + .\Debug\x32/ + true + Level3 + true + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + .\Debug\x32\botlib.lib + true + + + + + ../../../bin/win32/debug/botlib.dll + .\botlib.def + Windows + .\Debug\x32\botlib.lib + + + + + Disabled + WIN32;_WIN64;__WIN64__;_DEBUG;BOTLIB;DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + .\Debug\x64\botlib.pch + .\Debug\x64\ + .\Debug\x64\ + .\Debug\x64\ + true + Level3 + true + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + .\Debug\x64\botlib.lib + true + + + ../../../bin/win64/debug/botlib.dll + .\Debug\x64\botlib.lib + .\botlib.def + Windows + + + + + false + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/engine/botlib/botlib.vcxproj.filters b/src/engine/botlib/botlib.vcxproj.filters new file mode 100644 index 0000000000..f56e568541 --- /dev/null +++ b/src/engine/botlib/botlib.vcxproj.filters @@ -0,0 +1,218 @@ + + + + + {8728cfd0-f279-4c17-bef9-021a27a3cad5} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {dabb86ed-b181-4f2a-a216-1ab4154c3bc2} + h;hpp;hxx;hm;inl + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/src/engine/botlib/botlib_stub.c b/src/engine/botlib/botlib_stub.c new file mode 100644 index 0000000000..56421b1c8d --- /dev/null +++ b/src/engine/botlib/botlib_stub.c @@ -0,0 +1,285 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +/* + * $Id: botlib_stub.c,v 1.3 2004/01/31 04:03:49 ikkyo Exp $ + * + * rain - this is a stub botlib so that we can compile without changes to + * the rest of the engine. This way, we can drop in the real botlib later + * if we get access to it. + * + * Notes: + * + The l_* files are pilfered from extractfuncs. They work, but + * I believe they're a bit out-of-date versus the real versions.. + * + I don't yet return real handles for the PC functions--instead, I + * pass around the real pointer to the source_t struct. This is + * probably a bad thing, and it should be fixed down the road. + */ + +#include "../game/q_shared.h" +#include "../game/botlib.h" +#include "l_script.h" +#include "l_precomp.h" + +void botlib_stub(void); +int PC_LoadSourceHandle(const char *); +int PC_FreeSourceHandle(int); +int PC_ReadTokenHandle(int, pc_token_t *); +int PC_SourceFileAndLine(int, char *, int *); +void PC_UnreadLastTokenHandle(int); + +botlib_import_t botimport; + +botlib_export_t *GetBotLibAPI(int version, botlib_import_t * imports) +{ + static botlib_export_t botlib_export; + + botimport = *imports; + + botlib_export.aas.AAS_EntityInfo = (void *)botlib_stub; + botlib_export.aas.AAS_Initialized = (void *)botlib_stub; + botlib_export.aas.AAS_PresenceTypeBoundingBox = (void *)botlib_stub; + botlib_export.aas.AAS_Time = (void *)botlib_stub; + botlib_export.aas.AAS_PointAreaNum = (void *)botlib_stub; + botlib_export.aas.AAS_TraceAreas = (void *)botlib_stub; + botlib_export.aas.AAS_BBoxAreas = (void *)botlib_stub; + botlib_export.aas.AAS_AreaCenter = (void *)botlib_stub; + botlib_export.aas.AAS_AreaWaypoint = (void *)botlib_stub; + botlib_export.aas.AAS_PointContents = (void *)botlib_stub; + botlib_export.aas.AAS_NextBSPEntity = (void *)botlib_stub; + botlib_export.aas.AAS_ValueForBSPEpairKey = (void *)botlib_stub; + botlib_export.aas.AAS_VectorForBSPEpairKey = (void *)botlib_stub; + botlib_export.aas.AAS_FloatForBSPEpairKey = (void *)botlib_stub; + botlib_export.aas.AAS_IntForBSPEpairKey = (void *)botlib_stub; + botlib_export.aas.AAS_AreaReachability = (void *)botlib_stub; + botlib_export.aas.AAS_AreaLadder = (void *)botlib_stub; + botlib_export.aas.AAS_AreaTravelTimeToGoalArea = (void *)botlib_stub; + botlib_export.aas.AAS_Swimming = (void *)botlib_stub; + botlib_export.aas.AAS_PredictClientMovement = (void *)botlib_stub; + botlib_export.aas.AAS_RT_ShowRoute = (void *)botlib_stub; + botlib_export.aas.AAS_RT_GetHidePos = (void *)botlib_stub; + botlib_export.aas.AAS_FindAttackSpotWithinRange = (void *)botlib_stub; + botlib_export.aas.AAS_ListAreasInRange = (void *)botlib_stub; + botlib_export.aas.AAS_AvoidDangerArea = (void *)botlib_stub; + botlib_export.aas.AAS_Retreat = (void *)botlib_stub; + botlib_export.aas.AAS_AlternativeRouteGoals = (void *)botlib_stub; + botlib_export.aas.AAS_SetAASBlockingEntity = (void *)botlib_stub; + botlib_export.aas.AAS_NearestHideArea = (void *)botlib_stub; + botlib_export.aas.AAS_RecordTeamDeathArea = (void *)botlib_stub; + botlib_export.aas.AAS_SetCurrentWorld = (void *)botlib_stub; + + botlib_export.ea.EA_Say = (void *)botlib_stub; + botlib_export.ea.EA_SayTeam = (void *)botlib_stub; + botlib_export.ea.EA_UseItem = (void *)botlib_stub; + botlib_export.ea.EA_DropItem = (void *)botlib_stub; + botlib_export.ea.EA_UseInv = (void *)botlib_stub; + botlib_export.ea.EA_DropInv = (void *)botlib_stub; + botlib_export.ea.EA_Gesture = (void *)botlib_stub; + botlib_export.ea.EA_Command = (void *)botlib_stub; + botlib_export.ea.EA_SelectWeapon = (void *)botlib_stub; + botlib_export.ea.EA_Talk = (void *)botlib_stub; + botlib_export.ea.EA_Attack = (void *)botlib_stub; + botlib_export.ea.EA_Reload = (void *)botlib_stub; + botlib_export.ea.EA_Use = (void *)botlib_stub; + botlib_export.ea.EA_Respawn = (void *)botlib_stub; + botlib_export.ea.EA_Jump = (void *)botlib_stub; + botlib_export.ea.EA_DelayedJump = (void *)botlib_stub; + botlib_export.ea.EA_Crouch = (void *)botlib_stub; + botlib_export.ea.EA_Walk = (void *)botlib_stub; + botlib_export.ea.EA_MoveUp = (void *)botlib_stub; + botlib_export.ea.EA_MoveDown = (void *)botlib_stub; + botlib_export.ea.EA_MoveForward = (void *)botlib_stub; + botlib_export.ea.EA_MoveBack = (void *)botlib_stub; + botlib_export.ea.EA_MoveLeft = (void *)botlib_stub; + botlib_export.ea.EA_MoveRight = (void *)botlib_stub; + botlib_export.ea.EA_Move = (void *)botlib_stub; + botlib_export.ea.EA_View = (void *)botlib_stub; + botlib_export.ea.EA_Prone = (void *)botlib_stub; + botlib_export.ea.EA_EndRegular = (void *)botlib_stub; + botlib_export.ea.EA_GetInput = (void *)botlib_stub; + botlib_export.ea.EA_ResetInput = (void *)botlib_stub; + + botlib_export.ai.BotLoadCharacter = (void *)botlib_stub; + botlib_export.ai.BotFreeCharacter = (void *)botlib_stub; + botlib_export.ai.Characteristic_Float = (void *)botlib_stub; + botlib_export.ai.Characteristic_BFloat = (void *)botlib_stub; + botlib_export.ai.Characteristic_Integer = (void *)botlib_stub; + botlib_export.ai.Characteristic_BInteger = (void *)botlib_stub; + botlib_export.ai.Characteristic_String = (void *)botlib_stub; + botlib_export.ai.BotAllocChatState = (void *)botlib_stub; + botlib_export.ai.BotFreeChatState = (void *)botlib_stub; + botlib_export.ai.BotQueueConsoleMessage = (void *)botlib_stub; + botlib_export.ai.BotRemoveConsoleMessage = (void *)botlib_stub; + botlib_export.ai.BotNextConsoleMessage = (void *)botlib_stub; + botlib_export.ai.BotNumConsoleMessages = (void *)botlib_stub; + botlib_export.ai.BotInitialChat = (void *)botlib_stub; + botlib_export.ai.BotNumInitialChats = (void *)botlib_stub; + botlib_export.ai.BotReplyChat = (void *)botlib_stub; + botlib_export.ai.BotChatLength = (void *)botlib_stub; + botlib_export.ai.BotEnterChat = (void *)botlib_stub; + botlib_export.ai.BotGetChatMessage = (void *)botlib_stub; + botlib_export.ai.StringContains = (void *)botlib_stub; + botlib_export.ai.BotFindMatch = (void *)botlib_stub; + botlib_export.ai.BotMatchVariable = (void *)botlib_stub; + botlib_export.ai.UnifyWhiteSpaces = (void *)botlib_stub; + botlib_export.ai.BotReplaceSynonyms = (void *)botlib_stub; + botlib_export.ai.BotLoadChatFile = (void *)botlib_stub; + botlib_export.ai.BotSetChatGender = (void *)botlib_stub; + botlib_export.ai.BotSetChatName = (void *)botlib_stub; + botlib_export.ai.BotResetGoalState = (void *)botlib_stub; + botlib_export.ai.BotResetAvoidGoals = (void *)botlib_stub; + botlib_export.ai.BotRemoveFromAvoidGoals = (void *)botlib_stub; + botlib_export.ai.BotPushGoal = (void *)botlib_stub; + botlib_export.ai.BotPopGoal = (void *)botlib_stub; + botlib_export.ai.BotEmptyGoalStack = (void *)botlib_stub; + botlib_export.ai.BotDumpAvoidGoals = (void *)botlib_stub; + botlib_export.ai.BotDumpGoalStack = (void *)botlib_stub; + botlib_export.ai.BotGoalName = (void *)botlib_stub; + botlib_export.ai.BotGetTopGoal = (void *)botlib_stub; + botlib_export.ai.BotGetSecondGoal = (void *)botlib_stub; + botlib_export.ai.BotChooseLTGItem = (void *)botlib_stub; + botlib_export.ai.BotChooseNBGItem = (void *)botlib_stub; + botlib_export.ai.BotTouchingGoal = (void *)botlib_stub; + botlib_export.ai.BotItemGoalInVisButNotVisible = (void *)botlib_stub; + botlib_export.ai.BotGetLevelItemGoal = (void *)botlib_stub; + botlib_export.ai.BotGetNextCampSpotGoal = (void *)botlib_stub; + botlib_export.ai.BotGetMapLocationGoal = (void *)botlib_stub; + botlib_export.ai.BotAvoidGoalTime = (void *)botlib_stub; + botlib_export.ai.BotInitLevelItems = (void *)botlib_stub; + botlib_export.ai.BotUpdateEntityItems = (void *)botlib_stub; + botlib_export.ai.BotLoadItemWeights = (void *)botlib_stub; + botlib_export.ai.BotFreeItemWeights = (void *)botlib_stub; + botlib_export.ai.BotInterbreedGoalFuzzyLogic = (void *)botlib_stub; + botlib_export.ai.BotSaveGoalFuzzyLogic = (void *)botlib_stub; + botlib_export.ai.BotMutateGoalFuzzyLogic = (void *)botlib_stub; + botlib_export.ai.BotAllocGoalState = (void *)botlib_stub; + botlib_export.ai.BotFreeGoalState = (void *)botlib_stub; + botlib_export.ai.BotResetMoveState = (void *)botlib_stub; + botlib_export.ai.BotMoveToGoal = (void *)botlib_stub; + botlib_export.ai.BotMoveInDirection = (void *)botlib_stub; + botlib_export.ai.BotResetAvoidReach = (void *)botlib_stub; + botlib_export.ai.BotResetLastAvoidReach = (void *)botlib_stub; + botlib_export.ai.BotReachabilityArea = (void *)botlib_stub; + botlib_export.ai.BotMovementViewTarget = (void *)botlib_stub; + botlib_export.ai.BotPredictVisiblePosition = (void *)botlib_stub; + botlib_export.ai.BotAllocMoveState = (void *)botlib_stub; + botlib_export.ai.BotFreeMoveState = (void *)botlib_stub; + botlib_export.ai.BotInitMoveState = (void *)botlib_stub; + botlib_export.ai.BotInitAvoidReach = (void *)botlib_stub; + botlib_export.ai.BotChooseBestFightWeapon = (void *)botlib_stub; + botlib_export.ai.BotGetWeaponInfo = (void *)botlib_stub; + botlib_export.ai.BotLoadWeaponWeights = (void *)botlib_stub; + botlib_export.ai.BotAllocWeaponState = (void *)botlib_stub; + botlib_export.ai.BotFreeWeaponState = (void *)botlib_stub; + botlib_export.ai.BotResetWeaponState = (void *)botlib_stub; + botlib_export.ai.GeneticParentsAndChildSelection = (void *)botlib_stub; + + botlib_export.BotLibSetup = (void *)botlib_stub; + botlib_export.BotLibShutdown = (void *)botlib_stub; + botlib_export.BotLibVarSet = (void *)botlib_stub; + botlib_export.BotLibVarGet = (void *)botlib_stub; + botlib_export.BotLibStartFrame = (void *)botlib_stub; + botlib_export.BotLibLoadMap = (void *)botlib_stub; + botlib_export.BotLibUpdateEntity = (void *)botlib_stub; + botlib_export.Test = (void *)botlib_stub; + + botlib_export.PC_AddGlobalDefine = PC_AddGlobalDefine; + botlib_export.PC_RemoveAllGlobalDefines = PC_RemoveAllGlobalDefines; + botlib_export.PC_LoadSourceHandle = PC_LoadSourceHandle; + botlib_export.PC_FreeSourceHandle = PC_FreeSourceHandle; + botlib_export.PC_ReadTokenHandle = PC_ReadTokenHandle; + botlib_export.PC_SourceFileAndLine = PC_SourceFileAndLine; + botlib_export.PC_UnreadLastTokenHandle = PC_UnreadLastTokenHandle; + + + return &botlib_export; +} + +void botlib_stub(void) +{ + botimport.Print(PRT_WARNING, "WARNING: botlib stub!\n"); +} + +int PC_LoadSourceHandle(const char *filename) +{ + // rain - FIXME - LoadSourceFile should take a const filename + return (int)LoadSourceFile(filename); +} + +int PC_FreeSourceHandle(int handle) +{ + FreeSource((source_t *) handle); + return 0; +} + +int PC_ReadTokenHandle(int handle, pc_token_t * token) +{ + token_t t; + int ret; + + ret = PC_ReadToken((source_t *) handle, &t); + + token->type = t.type; + token->subtype = t.subtype; + token->intvalue = t.intvalue; + token->floatvalue = t.floatvalue; + Q_strncpyz(token->string, t.string, MAX_TOKENLENGTH); + token->line = t.line; + token->linescrossed = t.linescrossed; + + // gamecode doesn't want the quotes on the string + if(token->type == TT_STRING) + { + StripDoubleQuotes(token->string); + } + + return ret; +} + +int PC_SourceFileAndLine(int handle, char *filename, int *line) +{ + source_t *source = (source_t *) handle; + + Q_strncpyz(filename, source->filename, 128); + // ikkyo - i'm pretty sure token.line is the line of the last token + // parsed, not the line of the token currently being parsed... + *line = source->token.line; + + return 0; +} + +void PC_UnreadLastTokenHandle(int handle) +{ + PC_UnreadLastToken((source_t *) handle); +} diff --git a/src/engine/botlib/chars.h b/src/engine/botlib/chars.h new file mode 100644 index 0000000000..7754450fdb --- /dev/null +++ b/src/engine/botlib/chars.h @@ -0,0 +1,165 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +//=========================================================================== +// +// Name: chars.h +// Function: bot characteristics +// Programmer: digibob +// Last update: 2003-03-27 +// Tab Size: 4 (real tabs) +//=========================================================================== + +//unless specified, the higher the number, the better the characteristic, or +//the greater the likelyhood of the characteristic occuring. +//also, if any characteristic is ommited, the default value will be used from +//the file in /bots/default_c.c + +//======================================================== +//======================================================== +//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_FLAMETHROWER 12 +#define CHARACTERISTIC_AIM_ACCURACY_SP5 13 //float [0, 1] +#define CHARACTERISTIC_AIM_ACCURACY_SNIPERRIFLE 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_SP5 19 //float [0, 1] +//#define CHARACTERISTIC_AIM_SKILL_BFG10K 20 //float [0, 1] +#define CHARACTERISTIC_AIM_SKILL_SNIPERRIFLE 20 +//======================================================== +//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] + +//======================================================== +//======================================================== +// Gordon: adding new aim accuracies... this file needs cleaned up... +#define CHARACTERISTIC_AIM_ACCURACY_PRONEMG42 49 //float [0, 1] diff --git a/src/engine/botlib/inv.h b/src/engine/botlib/inv.h new file mode 100644 index 0000000000..f42ff2c18c --- /dev/null +++ b/src/engine/botlib/inv.h @@ -0,0 +1,142 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +/* + * name: inv.h + * + * desc: + * +*/ + + +#define INVENTORY_NONE 0 +//armor +#define INVENTORY_ARMOR 1 +//weapons +#define INVENTORY_LUGER 4 +#define INVENTORY_MP40 6 +#define INVENTORY_DYNAMITE 7 +#define INVENTORY_GRENADELAUNCHER 8 +#define INVENTORY_FLAMETHROWER 10 +#define INVENTORY_PANZERFAUST 11 +#define INVENTORY_COLT 12 +#define INVENTORY_THOMPSON 13 +#define INVENTORY_GRENADE_PINEAPPLE 15 +#define INVENTORY_STEN 17 + +//special "powers" +#define INVENTORY_MEDIC_SYRINGE 20 +#define INVENTORY_AMMO 21 +#define INVENTORY_MEDKIT 22 +#define INVENTORY_PLIERS 23 +#define INVENTORY_SMOKE_GRENADE 24 + + +// please leave these open up to 27 (INVENTORY_9MM) (and double check defines when merging) +// the inventory max (MAX_ITEMS) is 256, so we aren't too concerned about running out of space + +//ammo +#define INVENTORY_9MM 27 +#define INVENTORY_45CAL 28 +#define INVENTORY_PANZERFAUST_AMMO 33 +#define INVENTORY_FUEL 34 +#define INVENTORY_GRENADES 35 +#define INVENTORY_GRENADES_AMERICAN 36 +#define INVENTORY_DYNAMITE_AMMO 37 +// Mad Doc - TDF +// @TODO make an inventory for whatever we decide to use for BAR ammo. Bots can't use M1? + + +// please leave these open up to 48 (INVENTORY_HEALTH) (and double check defines when merging) +// the inventory max (MAX_ITEMS) is 256, so we aren't too concerned about running out of space + +//powerups +#define INVENTORY_HEALTH 48 +#define INVENTORY_TELEPORTER 49 +#define INVENTORY_QUAD 51 +#define INVENTORY_ENVIRONMENTSUIT 52 +#define INVENTORY_HASTE 53 +#define INVENTORY_INVISIBILITY 54 +#define INVENTORY_REGEN 55 +#define INVENTORY_FLIGHT 56 +#define INVENTORY_REDFLAG 57 +#define INVENTORY_BLUEFLAG 58 + + +//enemy stuff +#define ENEMY_HORIZONTAL_DIST 200 +#define ENEMY_HEIGHT 201 +#define NUM_VISIBLE_ENEMIES 202 +#define NUM_VISIBLE_TEAMMATES 203 +#define GOAL_TRAVELTIME 204 + +//item numbers (make sure they are in sync with bg_itemlist in bg_misc.c) +#define MODELINDEX_ARMORSHARD 1 +#define MODELINDEX_ARMORCOMBAT 2 +#define MODELINDEX_ARMORBODY 3 +#define MODELINDEX_HEALTHSMALL 4 +#define MODELINDEX_HEALTH 5 +#define MODELINDEX_HEALTHLARGE 6 +#define MODELINDEX_HEALTHMEGA 7 + +#define MODELINDEX_GAUNTLET 8 +#define MODELINDEX_SHOTGUN 9 +#define MODELINDEX_MACHINEGUN 10 +#define MODELINDEX_GRENADELAUNCHER 11 +#define MODELINDEX_ROCKETLAUNCHER 12 +#define MODELINDEX_LIGHTNING 13 +#define MODELINDEX_RAILGUN 14 +#define MODELINDEX_SP5 15 +#define MODELINDEX_BFG10K 16 +#define MODELINDEX_GRAPPLINGHOOK 17 + +#define MODELINDEX_SHELLS 18 +#define MODELINDEX_BULLETS 19 +#define MODELINDEX_GRENADES 20 +#define MODELINDEX_CELLS 21 +#define MODELINDEX_LIGHTNINGAMMO 22 +#define MODELINDEX_ROCKETS 23 +#define MODELINDEX_SLUGS 24 +#define MODELINDEX_BFGAMMO 25 + +#define MODELINDEX_TELEPORTER 26 +#define MODELINDEX_MEDKIT 27 +#define MODELINDEX_QUAD 28 +#define MODELINDEX_ENVIRONMENTSUIT 29 +#define MODELINDEX_HASTE 30 +#define MODELINDEX_INVISIBILITY 31 +#define MODELINDEX_REGEN 32 +#define MODELINDEX_FLIGHT 33 +#define MODELINDEX_REDFLAG 34 +#define MODELINDEX_BLUEFLAG 35 diff --git a/src/engine/botlib/l_common.h b/src/engine/botlib/l_common.h new file mode 100644 index 0000000000..ebf8702033 --- /dev/null +++ b/src/engine/botlib/l_common.h @@ -0,0 +1,40 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef _L_COMMON_ +#define _L_COMMON_ + +#error CVS REMOVE ME + +#endif diff --git a/src/engine/botlib/l_crc.c b/src/engine/botlib/l_crc.c new file mode 100644 index 0000000000..438540d60b --- /dev/null +++ b/src/engine/botlib/l_crc.c @@ -0,0 +1,170 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_crc.c + * + * desc: CRC calculation + * + * + *****************************************************************************/ + +#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, + 0x0000 // code reaches element 256 +}; + +//=========================================================================== +// +// 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/src/engine/botlib/l_crc.h b/src/engine/botlib/l_crc.h new file mode 100644 index 0000000000..fe4b46c461 --- /dev/null +++ b/src/engine/botlib/l_crc.h @@ -0,0 +1,50 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +//=========================================================================== +// +// Name: l_crc.h +// Function: for CRC checks +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-31 +// Tab Size: 3 +//=========================================================================== + +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/src/engine/botlib/l_libvar.c b/src/engine/botlib/l_libvar.c new file mode 100644 index 0000000000..82adc2a926 --- /dev/null +++ b/src/engine/botlib/l_libvar.c @@ -0,0 +1,319 @@ +/* +=========================================================================== + +Wolfenstein: Enemy Territory GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the Wolfenstein: Enemy Territory GPL Source Code (“Wolf ET Source Code”). + +Wolf ET Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Wolf ET 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 Wolf ET Source Code. If not, see . + +In addition, the Wolf: ET Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Wolf ET Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_libvar.c + * + * desc: bot library variables + * + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "l_memory.h" +#include "l_libvar.h" + +//list with library variables +libvar_t *libvarlist; + +//=========================================================================== +// +// 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) + strlen(var_name) + 1); + memset(v, 0, sizeof(libvar_t)); + v->name = (char *)v + sizeof(libvar_t); + 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); +} //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/src/engine/botlib/l_libvar.h b/src/engine/botlib/l_libvar.h new file mode 100644 index 0000000000..f7a5e5fa4d --- /dev/null +++ b/src/engine/botlib/l_libvar.h @@ -0,0 +1,83 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_libvar.h + * + * desc: botlib vars + * + * + *****************************************************************************/ + +//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/src/engine/botlib/l_log.c b/src/engine/botlib/l_log.c new file mode 100644 index 0000000000..7e90ed17e9 --- /dev/null +++ b/src/engine/botlib/l_log.c @@ -0,0 +1,214 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_log.c + * + * desc: log file + * + * + *****************************************************************************/ + +#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_AlwaysOpen(char *filename) +{ + 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_Open(char *filename) +{ + if(!LibVarValue("log", "0")) + { + return; + } + Log_AlwaysOpen(filename); + +} + +//=========================================================================== +// +// 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/src/engine/botlib/l_log.h b/src/engine/botlib/l_log.h new file mode 100644 index 0000000000..12c00caa29 --- /dev/null +++ b/src/engine/botlib/l_log.h @@ -0,0 +1,66 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_log.h + * + * desc: log file + * + * + *****************************************************************************/ + +//open a log file +void Log_Open(char *filename); + +// +void Log_AlwaysOpen(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/src/engine/botlib/l_memory.c b/src/engine/botlib/l_memory.c new file mode 100644 index 0000000000..b178398313 --- /dev/null +++ b/src/engine/botlib/l_memory.c @@ -0,0 +1,503 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_memory.c + * + * desc: memory allocation + * + * + *****************************************************************************/ + +#include "../qcommon/q_shared.h" +#include "botlib.h" +#include "l_log.h" +#include "be_interface.h" + +#ifdef _DEBUG +// #define MEMDEBUG +#define MEMORYMANEGER +#endif + +#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; + + 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 + 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 + 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 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("\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 + 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 + 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: - +//=========================================================================== +void PrintUsedMemorySize(void) +{ +} //end of the function PrintUsedMemorySize + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintMemoryLabels(void) +{ +} //end of the function PrintMemoryLabels + +#endif diff --git a/src/engine/botlib/l_memory.h b/src/engine/botlib/l_memory.h new file mode 100644 index 0000000000..4fd8817f2c --- /dev/null +++ b/src/engine/botlib/l_memory.h @@ -0,0 +1,97 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +/***************************************************************************** + * name: l_memory.h + * + * desc: memory management + * + * + *****************************************************************************/ + +#ifdef _DEBUG +// #define MEMDEBUG +#endif + +#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); + +//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/src/engine/botlib/l_precomp.c b/src/engine/botlib/l_precomp.c new file mode 100644 index 0000000000..dae01aa499 --- /dev/null +++ b/src/engine/botlib/l_precomp.c @@ -0,0 +1,3888 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +/***************************************************************************** + * name: l_precomp.c + * + * desc: pre compiler + * + * + *****************************************************************************/ + +//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 + +#define MAX_TOKENLENGTH 1024 + +typedef struct pc_token_s +{ + int type; + int subtype; + int intvalue; + int line; + int linescrossed; + float floatvalue; + char string[MAX_TOKENLENGTH]; +} pc_token_t; +#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 *) GetMemory(sizeof(token_t)); + 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; + 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) +{ + FreeMemory(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 + 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: - +//============================================================================ +define_t *PC_FindHashedDefine(define_t ** definehash, char *name); +int PC_ExpandDefineIntoSource(source_t * source, token_t * deftoken, define_t * define); +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; + define_t *newdefine; + + 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 = 1; !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 <= 1) + { + if(lastcomma) + { + SourceWarning(source, "too many comma's"); + } + lastcomma = 1; + break; + } //end if + } //end if + lastcomma = 0; + // + if(!strcmp(token.string, "(")) + { + indent++; + } //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 + else if(token.type == TT_NAME) + { + newdefine = PC_FindHashedDefine(source->definehash, token.string); + if(newdefine) + { + if(!PC_ExpandDefineIntoSource(source, &token, newdefine)) + { + return qfalse; + } + continue; + } + } // 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)); + } //end for + strncat(token->string, "\"", MAX_TOKEN - strlen(token->string)); + 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) +{ + register int 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); +} //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) + strlen(builtin[i].string) + 1); + memset(define, 0, sizeof(define_t)); + define->name = (char *)define + sizeof(define_t); + 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; +#if defined IPHONE || defined __FreeBSD__ + time_t t; +#else + unsigned long t; // time_t t; //to prevent LCC warning +#endif // IPHONE + 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); + } //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) + { + 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) + strlen(token.string) + 1); + memset(define, 0, sizeof(define_t)); + define->name = (char *)define + sizeof(define_t); + 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 + 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 if 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 + + globaldefines = NULL; +} //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) + strlen(define->name) + 1); + //copy the define name + newdefine->name = (char *)newdefine + sizeof(define_t); + 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; + double 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, double *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; + double 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, double *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, double *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) +{ + double 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", Q_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) +{ + double 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", Q_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 + //if skipping source because of conditional compilation + if(source->skip) + { + continue; + } + // recursively concatenate strings that are behind each other + 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 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 + 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) + { + 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)); + 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)); + 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; + pc_token->line = token.line; + pc_token->linescrossed = token.linescrossed; + if(pc_token->type == TT_STRING) + { + StripDoubleQuotes(pc_token->string); + } + return ret; +} //end of the function PC_ReadTokenHandle + +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_UnreadLastTokenHandle(int handle) +{ + if(handle < 1 || handle >= MAX_SOURCEFILES) + { + return; + } + if(!sourceFiles[handle]) + { + return; + } + + PC_UnreadSourceToken(sourceFiles[handle], &sourceFiles[handle]->token); +} //end of the function PC_UnreadLastTokenHandle + +//============================================================================ +// +// 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/src/engine/botlib/l_precomp.h b/src/engine/botlib/l_precomp.h new file mode 100644 index 0000000000..862170394a --- /dev/null +++ b/src/engine/botlib/l_precomp.h @@ -0,0 +1,196 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_precomp.h + * + * desc: pre compiler + * + * + *****************************************************************************/ + +#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 + + +#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[_MAX_PATH]; //file name of the script + char includepath[_MAX_PATH]; //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, ...); + +// +int PC_LoadSourceHandle(const char *filename); +int PC_FreeSourceHandle(int handle); +int PC_ReadTokenHandle(int handle, struct pc_token_s *pc_token); +int PC_SourceFileAndLine(int handle, char *filename, int *line); +void PC_CheckOpenSourceHandles(void); +void PC_UnreadLastTokenHandle(int handle); diff --git a/src/engine/botlib/l_script.c b/src/engine/botlib/l_script.c new file mode 100644 index 0000000000..2a8aa14099 --- /dev/null +++ b/src/engine/botlib/l_script.c @@ -0,0 +1,1703 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_script.c + * + * desc: lexicographical parser + * + * + *****************************************************************************/ + +//#define SCREWUP +//#define BOTLIB +//#define MEQCC +//#define BSPC + +#ifdef SCREWUP +#include +#include +#include +#include +#include +#include "../botlib/l_memory.h" +#include "../botlib/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 *)); + } + 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, long double *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 + (long double)(*string - '0') / (long double)dotfound; + dotfound *= 10; + } //end if + else + { + *floatvalue = *floatvalue * 10.0 + (long double)(*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; +// long 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; + //hexadecimal + 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 + 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; + 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 + 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 + 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) + { + 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) +{ + 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); + // rain - strcpy arguments cannot overlap, memmove string+1 and NUL + memmove(string, string + 1, strlen(string + 1) + 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: - +//============================================================================ +long double ReadSignedFloat(script_t * script) +{ + token_t token; + long double sign = 1; + + PS_ExpectAnyToken(script, &token); + if(!strcmp(token.string, "-")) + { + sign = -1; + 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 + 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: - +//============================================================================ +int COM_Compress(char *data_p); +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; + 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; + 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); + // + 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 + Q_strncpyz(basefolder, path, sizeof(basefolder)); +#endif +} //end of the function PS_SetBaseFolder diff --git a/src/engine/botlib/l_script.h b/src/engine/botlib/l_script.h new file mode 100644 index 0000000000..1ebe2c8c58 --- /dev/null +++ b/src/engine/botlib/l_script.h @@ -0,0 +1,296 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_script.h + * + * desc: lexicographical parser + * + * + *****************************************************************************/ + +// Ridah, can't get it to compile without this +#ifndef QDECL + +// for windows fastcall option +#define QDECL +//======================= WIN32 DEFINES ================================= +#ifdef WIN32 +#undef QDECL +#define QDECL __cdecl +#endif +#endif +// done. + +//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 +//maximum path length +#ifndef _MAX_PATH +// TTimo: used to be MAX_QPATH, which is the game filesystem max len, and not the OS max len +#define _MAX_PATH 1024 +#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 + long double 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[_MAX_PATH]; //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 +long double 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/src/engine/botlib/l_struct.c b/src/engine/botlib/l_struct.c new file mode 100644 index 0000000000..5ab0db4cb7 --- /dev/null +++ b/src/engine/botlib/l_struct.c @@ -0,0 +1,620 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_struct.c + * + * desc: structure reading / writing + * + * + *****************************************************************************/ + +#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; + + sprintf(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/src/engine/botlib/l_struct.h b/src/engine/botlib/l_struct.h new file mode 100644 index 0000000000..ef9a86166f --- /dev/null +++ b/src/engine/botlib/l_struct.h @@ -0,0 +1,88 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_struct.h + * + * desc: structure reading/writing + * + * + *****************************************************************************/ + + +#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/src/engine/botlib/l_utils.h b/src/engine/botlib/l_utils.h new file mode 100644 index 0000000000..5f5c165a7f --- /dev/null +++ b/src/engine/botlib/l_utils.h @@ -0,0 +1,49 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_util.h + * + * desc: utils + * + * + *****************************************************************************/ + +#define Vector2Angles( v,a ) vectoangles( v,a ) +#ifndef MAX_PATH // LBO 1/25/05 +#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/src/engine/botlib/match.h b/src/engine/botlib/match.h new file mode 100644 index 0000000000..f06b63daf0 --- /dev/null +++ b/src/engine/botlib/match.h @@ -0,0 +1,138 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +//=========================================================================== +// +// Name: match.h +// Function: match template defines +// Programmer: Mr Elusive (MrElusive@idsoftware.com) +// Last update: 1999-10-01 +// Tab Size: 4 (real tabs) +// +//=========================================================================== + +//match template contexts +#define MTCONTEXT_ENTERGAME 2 +#define MTCONTEXT_INITIALTEAMCHAT 4 +#define MTCONTEXT_TIME 8 +#define MTCONTEXT_TEAMMATE 16 +#define MTCONTEXT_ADDRESSEE 32 +#define MTCONTEXT_PATROLKEYAREA 64 +#define MTCONTEXT_REPLYCHAT 128 +#define MTCONTEXT_CTF 256 + +//message types +#define MSG_ENTERGAME 2 //enter game message +#define MSG_HELP 3 //help someone +#define MSG_ACCOMPANY 4 //accompany someone +#define MSG_DEFENDKEYAREA 5 //defend a key area +#define MSG_RUSHBASE 6 //everyone rush to base +#define MSG_GETFLAG 7 //get the enemy flag +#define MSG_STARTTEAMLEADERSHIP 8 //someone wants to become the team leader +#define MSG_STOPTEAMLEADERSHIP 9 //someone wants to stop being the team leader +#define MSG_WHOISTEAMLAEDER 10 //who is the team leader +#define MSG_WAIT 11 //wait for someone +#define MSG_WHATAREYOUDOING 12 //what are you doing? +#define MSG_JOINSUBTEAM 13 //join a sub-team +#define MSG_LEAVESUBTEAM 14 //leave a sub-team +#define MSG_CREATENEWFORMATION 15 //create a new formation +#define MSG_FORMATIONPOSITION 16 //tell someone his/her position in a formation +#define MSG_FORMATIONSPACE 17 //set the formation intervening space +#define MSG_DOFORMATION 18 //form a known formation +#define MSG_DISMISS 19 //dismiss commanded team mates +#define MSG_CAMP 20 //camp somewhere +#define MSG_CHECKPOINT 21 //remember a check point +#define MSG_PATROL 22 //patrol between certain keypoints +#define MSG_LEADTHEWAY 23 //lead the way +#define MSG_GETITEM 24 //get an item +#define MSG_KILL 25 //kill someone +#define MSG_WHEREAREYOU 26 //where is someone +#define MSG_RETURNFLAG 27 //return the flag +#define MSG_WHATISMYCOMMAND 28 //ask the team leader what to do +#define MSG_WHICHTEAM 29 //ask which team a bot is in +// +#define MSG_ME 100 +#define MSG_EVERYONE 101 +#define MSG_MULTIPLENAMES 102 +#define MSG_NAME 103 +#define MSG_PATROLKEYAREA 104 +#define MSG_MINUTES 105 +#define MSG_SECONDS 106 +#define MSG_FOREVER 107 +// +#define MSG_CHATALL 200 +#define MSG_CHATTEAM 201 +// +#define MSG_CTF 300 //ctf message + +//command sub types +#define ST_SOMEWHERE 0 +#define ST_NEARITEM 1 +#define ST_ADDRESSED 2 +#define ST_METER 4 +#define ST_FEET 8 +#define ST_TIME 16 +#define ST_HERE 32 +#define ST_THERE 64 +#define ST_I 128 +#define ST_MORE 256 +#define ST_BACK 512 +#define ST_REVERSE 1024 +#define ST_SOMEONE 2048 +#define ST_GOTFLAG 4096 +#define ST_CAPTUREDFLAG 8192 +#define ST_RETURNEDFLAG 16384 +#define ST_TEAM 32768 + + +//word replacement variables +#define THE_ENEMY 7 +#define THE_TEAM 7 +//team message variables +#define NETNAME 0 +#define PLACE 1 +#define FLAG 1 +#define MESSAGE 2 +#define ADDRESSEE 2 +#define ITEM 3 +#define TEAMMATE 4 +#define TEAMNAME 4 +#define ENEMY 4 +#define KEYAREA 5 +#define FORMATION 5 +#define POSITION 5 +#define NUMBER 5 +#define TIME 6 +#define NAME 6 +#define MORE 6 diff --git a/src/engine/botlib/syn.h b/src/engine/botlib/syn.h new file mode 100644 index 0000000000..a74e03194d --- /dev/null +++ b/src/engine/botlib/syn.h @@ -0,0 +1,52 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +//=========================================================================== +// +// Name: syn.h +// Function: synonyms +// Programmer: Mr Elusive (MrElusive@idsoftware.com) +// Last update: 1999-09-08 +// Tab Size: 4 (real tabs) +// Notes: - +//=========================================================================== + +#define CONTEXT_ALL 0xFFFFFFFF +#define CONTEXT_NORMAL 1 +#define CONTEXT_NEARBYITEM 2 +#define CONTEXT_CTFREDTEAM 4 +#define CONTEXT_CTFBLUETEAM 8 +#define CONTEXT_REPLY 16 + +#define CONTEXT_NAMES 1024 diff --git a/src/engine/client/cg_api.h b/src/engine/client/cg_api.h new file mode 100644 index 0000000000..ee0eebd4f1 --- /dev/null +++ b/src/engine/client/cg_api.h @@ -0,0 +1,487 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 2011 Dusan Jocic + +OpenWolf 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. + +OpenWolf 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 this program. If not, see . + +=========================================================================== +*/ + +#include "../qcommon/q_shared.h" +#include "../renderer/tr_types.h" + +#define CGAME_IMPORT_API_VERSION 3 +#define CAM_PRIMARY 0 + + +#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 512 + +// 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; + + + +typedef enum cgameEvent_e { + CGAME_EVENT_NONE, + CGAME_EVENT_GAMEVIEW, + CGAME_EVENT_SPEAKEREDITOR, + CGAME_EVENT_CAMPAIGNBREIFING, + CGAME_EVENT_DEMO, + CGAME_EVENT_FIRETEAMMSG, + CGAME_EVENT_MULTIVIEW +} cgameEvent_t; + +typedef enum { + CG_PRINT, + CG_ERROR, + CG_MILLISECONDS, + CG_CVAR_REGISTER, + CG_CVAR_UPDATE, + CG_CVAR_SET, + CG_CVAR_VARIABLESTRINGBUFFER, + CG_CVAR_LATCHEDVARIABLESTRINGBUFFER, + CG_ARGC, + CG_ARGV, + CG_ARGS, + CG_LITERAL_ARGS, + CG_GETDEMOSTATE, + CG_GETDEMOPOS, + CG_FS_FOPENFILE, + CG_FS_READ, + CG_FS_WRITE, + CG_FS_FCLOSEFILE, + CG_FS_GETFILELIST, + CG_FS_DELETEFILE, + CG_SENDCONSOLECOMMAND, + CG_ADDCOMMAND, + CG_REMOVECOMMAND, + CG_SENDCLIENTCOMMAND, + CG_UPDATESCREEN, + CG_CM_LOADMAP, + CG_CM_NUMINLINEMODELS, + CG_CM_INLINEMODEL, + CG_CM_TEMPBOXMODEL, + CG_CM_TEMPCAPSULEMODEL, + CG_CM_POINTCONTENTS, + CG_CM_TRANSFORMEDPOINTCONTENTS, + CG_CM_BOXTRACE, + CG_CM_TRANSFORMEDBOXTRACE, + CG_CM_CAPSULETRACE, + CG_CM_TRANSFORMEDCAPSULETRACE, + CG_CM_BISPHERETRACE, + CG_CM_TRANSFORMEDBISPHERETRACE, + CG_CM_MARKFRAGMENTS, + CG_R_PROJECTDECAL, + CG_R_CLEARDECALS, + CG_S_STARTSOUND, + CG_S_STARTSOUNDEX, + CG_S_STARTLOCALSOUND, + CG_S_CLEARLOOPINGSOUNDS, + CG_S_CLEARSOUNDS, + CG_S_ADDLOOPINGSOUND, + CG_S_ADDREALLOOPINGSOUND, + CG_S_STOPLOOPINGSOUND, + CG_S_STOPSTREAMINGSOUND, + CG_S_UPDATEENTITYPOSITION, + CG_S_GETVOICEAMPLITUDE, + CG_S_GETSOUNDLENGTH, + CG_S_GETCURRENTSOUNDTIME, + CG_S_RESPATIALIZE, + CG_S_REGISTERSOUND, + CG_S_STARTBACKGROUNDTRACK, + CG_S_FADESTREAMINGSOUND, + CG_S_STARTSTREAMINGSOUND, + CG_R_LOADWORLDMAP, + CG_R_REGISTERMODEL, + CG_R_REGISTERSKIN, + CG_R_GETSKINMODEL, + CG_R_GETMODELSHADER, + CG_R_REGISTERSHADER, + CG_R_REGISTERFONT, + CG_R_REGISTERSHADERNOMIP, +#if defined(USE_REFLIGHT) + CG_R_REGISTERSHADERLIGHTATTENUATION, +#endif + CG_R_CLEARSCENE, + CG_R_ADDREFENTITYTOSCENE, +#if defined(USE_REFLIGHT) + CG_R_ADDREFLIGHTSTOSCENE, +#endif + CG_R_ADDPOLYTOSCENE, + CG_R_ADDPOLYSTOSCENE, + CG_R_ADDPOLYBUFFERTOSCENE, + CG_R_ADDLIGHTTOSCENE, + CG_R_ADDADDITIVELIGHTTOSCENE, + CG_FS_SEEK, + CG_R_ADDCORONATOSCENE, + CG_R_SETFOG, + CG_R_SETGLOBALFOG, + CG_R_RENDERSCENE, + CG_R_SAVEVIEWPARMS, + CG_R_RESTOREVIEWPARMS, + CG_R_SETCOLOR, + CG_R_SETCLIPREGION, + CG_R_DRAWSTRETCHPIC, + CG_R_DRAWROTATEDPIC, + CG_R_DRAWSTRETCHPIC_GRADIENT, + CG_R_DRAW2DPOLYS, + CG_R_MODELBOUNDS, + CG_R_LERPTAG, + CG_GETGLCONFIG, + CG_GETGAMESTATE, + CG_GETCURRENTSNAPSHOTNUMBER, + CG_GETSNAPSHOT, + CG_GETSERVERCOMMAND, + CG_GETCURRENTCMDNUMBER, + CG_GETUSERCMD, + CG_SETUSERCMDVALUE, + CG_SETCLIENTLERPORIGIN, + CG_MEMORY_REMAINING, + CG_KEY_ISDOWN, + CG_KEY_GETCATCHER, + CG_KEY_SETCATCHER, + CG_KEY_GETKEY, + CG_KEY_GETOVERSTRIKEMODE, + CG_KEY_SETOVERSTRIKEMODE, + CG_MEMSET, + CG_MEMCPY, + CG_STRNCPY, + CG_SIN, + CG_COS, + CG_ATAN2, + CG_SQRT, + CG_FLOOR, + CG_CEIL, + CG_ACOS, + CG_PC_ADD_GLOBAL_DEFINE, + CG_PC_LOAD_SOURCE, + CG_PC_FREE_SOURCE, + CG_PC_READ_TOKEN, + CG_PC_SOURCE_FILE_AND_LINE, + CG_PC_UNREAD_TOKEN, + CG_S_STOPBACKGROUNDTRACK, + CG_REAL_TIME, + CG_SNAPVECTOR, + CG_CIN_PLAYCINEMATIC, + CG_CIN_STOPCINEMATIC, + CG_CIN_RUNCINEMATIC, + CG_CIN_DRAWCINEMATIC, + CG_CIN_SETEXTENTS, + CG_R_REMAP_SHADER, + CG_TESTPRINTINT, + CG_TESTPRINTFLOAT, + CG_LOADCAMERA, + CG_STARTCAMERA, + CG_STOPCAMERA, + CG_GETCAMERAINFO, + CG_GET_ENTITY_TOKEN, + CG_INGAME_POPUP, + CG_INGAME_CLOSEPOPUP, + CG_KEY_GETBINDINGBUF, + CG_KEY_SETBINDING, + CG_PARSE_ADD_GLOBAL_DEFINE, + CG_PARSE_LOAD_SOURCE, + CG_PARSE_FREE_SOURCE, + CG_PARSE_READ_TOKEN, + CG_PARSE_SOURCE_FILE_AND_LINE, + CG_KEY_KEYNUMTOSTRINGBUF, + CG_KEY_BINDINGTOKEYS, + CG_TRANSLATE_STRING, + CG_S_FADEALLSOUNDS, + CG_R_INPVS, + CG_GETHUNKDATA, + CG_PUMPEVENTLOOP, + CG_SENDMESSAGE, + CG_MESSAGESTATUS, + CG_R_LOADDYNAMICSHADER, + CG_R_RENDERTOTEXTURE, + CG_R_GETTEXTUREID, + CG_R_FINISH, + CG_GETDEMONAME, + CG_R_LIGHTFORPOINT, + CG_S_SOUNDDURATION, +#if defined(USE_REFENTITY_ANIMATIONSYSTEM) + CG_R_REGISTERANIMATION, + CG_R_CHECKSKELETON, + CG_R_BUILDSKELETON, + CG_R_BLENDSKELETON, + CG_R_BONEINDEX, + CG_R_ANIMNUMFRAMES, + CG_R_ANIMFRAMERATE, +#endif + CG_COMPLETE_CALLBACK +} cgameImport_t; + +typedef enum { + CG_INIT, +// void CG_Init( int serverMessageNum, int serverCommandSequence ) + // 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_CONSOLE_TEXT, +// void (*CG_ConsoleText)( void ); + // pass text that has been printed to the console to cgame + // use Cmd_Argc() / Cmd_Argv() to read it + + 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, qboolean fForced); + + CG_GET_TAG, +// qboolean CG_GetTag( int clientNum, char *tagname, orientation_t *or ); + + CG_CHECKEXECKEY, + + CG_WANTSBINDKEYS, + + // zinx + CG_MESSAGERECEIVED, +// void (*CG_MessageReceived)( const char *buf, int buflen, int serverTime ); + // -zinx + CG_VOIP_STRING, +// char *(*CG_VoIPString)( void ); +// returns a string of comma-delimited clientnums based on cl_voipSendTarget + + CG_COMPLETE_COMMAND +// char (*CG_CompleteCommand)( int argNum ); +// will callback on all availible completions +// use Cmd_Argc() / Cmd_Argv() to read the command +} cgameExport_t; + +void trap_Print(const char *fmt); +void trap_Error(const char *fmt); +int trap_Milliseconds(void); +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); +void trap_Cvar_LatchedVariableStringBuffer(const char *var_name, char *buffer, int bufsize); +int trap_Argc(void); +void trap_Argv(int n, char *buffer, int bufferLength); +void trap_Args(char *buffer, int bufferLength); +void trap_LiteralArgs(char *buffer, int bufferLength); +int trap_GetDemoState(void); +int trap_GetDemoPos(void); +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_GetFileList(const char *path, const char *extension, char *listbuf, int bufsize); +int trap_FS_Delete(const char *filename); +void trap_SendConsoleCommand(const char *text); +void trap_AddCommand(const char *cmdName); +void trap_RemoveCommand(const char *cmdName); +void trap_SendClientCommand(const char *s); +void trap_UpdateScreen(void); +void trap_CM_LoadMap(const char *mapname); +int trap_CM_NumInlineModels(void); +clipHandle_t trap_CM_InlineModel(int index); +clipHandle_t trap_CM_TempBoxModel(const vec3_t mins, const vec3_t maxs); +clipHandle_t trap_CM_TempCapsuleModel(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); +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); +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); +void trap_CM_BiSphereTrace(trace_t *results, const vec3_t start, const vec3_t end, float startRad, float endRad, clipHandle_t model, int mask); +void trap_CM_TransformedBiSphereTrace(trace_t *results, const vec3_t start, const vec3_t end, float startRad, float endRad, clipHandle_t model, int mask, const vec3_t origin); +int trap_CM_MarkFragments(int numPoints, const vec3_t * points, const vec3_t projection, int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t * fragmentBuffer); +void trap_R_ProjectDecal(qhandle_t hShader, int numPoints, vec3_t * points, vec4_t projection, vec4_t color, int lifeTime, int fadeTime); +void trap_R_ClearDecals(void); +void trap_S_StartSound(vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx); +void trap_S_StartSoundEx(vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx, int flags); +void trap_S_StartLocalSound(sfxHandle_t sfx, int channelNum); +//void trap_S_ClearLoopingSounds(void); +void trap_S_ClearLoopingSounds(qboolean killall); +void trap_S_ClearSounds(qboolean killmusic); +//void trap_S_AddLoopingSound(const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx, int volume, int soundTime); +void trap_S_AddLoopingSound(int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx); +//void trap_S_AddRealLoopingSound(const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx, int range, int volume, int soundTime); +void trap_S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); +void trap_S_StopLoopingSound(int entityNum); +void trap_S_StopStreamingSound(int entityNum); +int trap_S_SoundDuration( sfxHandle_t handle ); +void trap_S_UpdateEntityPosition(int entityNum, const vec3_t origin); +int trap_S_GetVoiceAmplitude(int entityNum); +int trap_S_GetSoundLength(sfxHandle_t sfx); +int trap_S_GetCurrentSoundTime(void); +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); +void trap_S_StartBackgroundTrack(const char *intro, const char *loop); +void trap_S_FadeBackgroundTrack(float targetvol, int time, int num); +int trap_S_StartStreamingSound(const char *intro, const char *loop, int entnum, int channel, int attenuation); +void trap_R_LoadWorldMap(const char *mapname); +qhandle_t trap_R_RegisterModel(const char *name); +qhandle_t trap_R_RegisterSkin(const char *name); +qboolean trap_R_GetSkinModel(qhandle_t skinid, const char *type, char *name); +qhandle_t trap_R_GetShaderFromModel(qhandle_t modelid, int surfnum, int withlightmap); +qhandle_t trap_R_RegisterShader(const char *name); +void trap_R_RegisterFont(const char *fontName, int pointSize, fontInfo_t * font); +qhandle_t trap_R_RegisterShaderNoMip(const char *name); +qhandle_t trap_R_RegisterShaderLightAttenuation(const char *name); +void trap_R_ClearScene(void); +void trap_R_AddRefEntityToScene(const refEntity_t * re); +void trap_R_AddRefLightToScene(const refLight_t * light); +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_AddPolyBufferToScene(polyBuffer_t * pPolyBuffer); +void trap_R_AddLightToScene(const vec3_t org, float radius, float intensity, float r, float g, float b, qhandle_t hShader, int flags); +void trap_R_AddAdditiveLightToScene(const vec3_t org, float intensity, float r, float g, float b); +void trap_GS_FS_Seek(fileHandle_t f, long offset, fsOrigin_t origin); +void trap_R_AddCoronaToScene(const vec3_t org, float r, float g, float b, float scale, int id, qboolean visible); +void trap_R_SetFog(int fogvar, int var1, int var2, float r, float g, float b, float density); +void trap_R_SetGlobalFog(qboolean restore, int duration, float r, float g, float b, float depthForOpaque); +void trap_R_RenderScene(const refdef_t * fd); +void trap_R_SaveViewParms(); +void trap_R_RestoreViewParms(); +void trap_R_SetColor(const float *rgba); +void trap_R_SetClipRegion(const float *region); +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_DrawRotatedPic(float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader, float angle); +void trap_R_DrawStretchPicGradient(float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader, const float *gradientColor, int gradientType); +void trap_R_Add2dPolys(polyVert_t * verts, int numverts, qhandle_t hShader); +void trap_R_ModelBounds(clipHandle_t model, vec3_t mins, vec3_t maxs); +int trap_R_LerpTag(orientation_t * tag, const refEntity_t * refent, const char *tagName, int startIndex); +void trap_GetGlconfig(glconfig_t * glconfig); +void trap_GetGameState(gameState_t * gamestate); +void trap_GetCurrentSnapshotNumber(int *snapshotNumber, int *serverTime); +qboolean trap_GetSnapshot(int snapshotNumber, snapshot_t * snapshot); +qboolean trap_GetServerCommand(int serverCommandNumber); +int trap_GetCurrentCmdNumber(void); +qboolean trap_GetUserCmd(int cmdNumber, usercmd_t * ucmd); +void trap_SetUserCmdValue(int stateValue, int flags, float sensitivityScale, int mpIdentClient); +void trap_SetClientLerpOrigin(float x, float y, float z); +int trap_MemoryRemaining(void); +qboolean trap_Key_IsDown(int keynum); +int trap_Key_GetCatcher(void); +void trap_Key_SetCatcher(int catcher); +int trap_Key_GetKey(const char *binding); +qboolean trap_Key_GetOverstrikeMode(void); +void trap_Key_SetOverstrikeMode(qboolean state); +int trap_PC_AddGlobalDefine(char *define); +int trap_PC_LoadSource(const char *filename); +int trap_PC_FreeSource(int handle); +int trap_PC_ReadToken(int handle, pc_token_t * pc_token); +int trap_PC_SourceFileAndLine(int handle, char *filename, int *line); +int trap_PC_UnReadToken(int handle); +void trap_S_StopBackgroundTrack(void); +int trap_RealTime(qtime_t * qtime); +void trap_SnapVector(float *v); +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_R_RemapShader(const char *oldShader, const char *newShader, const char *timeOffset); +void testPrintInt(char *string, int i); +void testPrintFloat(char *string, float f); +qboolean trap_loadCamera(int camNum, const char *name); +void trap_startCamera(int camNum, int time); +void trap_stopCamera(int camNum); +qboolean trap_getCameraInfo(int camNum, int time, vec3_t * origin, vec3_t * angles, float *fov); +qboolean trap_GetEntityToken(char *buffer, int bufferSize); +void trap_UI_Popup(int arg0); +void trap_UI_ClosePopup(const char *arg0); +void trap_Key_GetBindingBuf(int keynum, char *buf, int buflen); +void trap_Key_SetBinding(int keynum, const char *binding); +int trap_Parse_AddGlobalDefine(char *define); +int trap_Parse_LoadSource(const char *filename); +int trap_Parse_FreeSource(int handle); +int trap_Parse_ReadToken(int handle, pc_token_t *pc_token); +int trap_Parse_SourceFileAndLine(int handle, char *filename, int *line); +void trap_Key_KeynumToStringBuf(int keynum, char *buf, int buflen); +void trap_Key_KeysForBinding(const char *binding, int *key1, int *key2); +void trap_CG_TranslateString(const char *string, char *buf); +void trap_S_FadeAllSound(float targetvol, int time, qboolean stopsounds); +qboolean trap_R_inPVS(const vec3_t p1, const vec3_t p2); +void trap_GetHunkData(int *hunkused, int *hunkexpected); +void trap_PumpEventLoop(void); +void trap_SendMessage(char *buf, int buflen); +messageStatus_t trap_MessageStatus(void); +qboolean trap_R_LoadDynamicShader(const char *shadername, const char *shadertext); +void trap_R_RenderToTexture(int textureid, int x, int y, int w, int h); +int trap_R_GetTextureId(const char *name); +void trap_R_Finish(void); +void trap_GetDemoName(char *buffer, int size); +void trap_S_StartSoundVControl(vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx, int volume); +int trap_R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ); +#if defined(USE_REFENTITY_ANIMATIONSYSTEM) +qhandle_t trap_R_RegisterAnimation(const char *name); +int trap_R_CheckSkeleton(refSkeleton_t * skel, qhandle_t hModel, qhandle_t hAnim); +int trap_R_BuildSkeleton(refSkeleton_t * skel, qhandle_t anim, int startFrame, int endFrame, float frac, qboolean clearOrigin); +int trap_R_BlendSkeleton(refSkeleton_t * skel, const refSkeleton_t * blend, float frac); +int trap_R_BoneIndex(qhandle_t hModel, const char *boneName); +int trap_R_AnimNumFrames(qhandle_t hAnim); +int trap_R_AnimFrameRate(qhandle_t hAnim); +#endif +void trap_CompleteCallback( const char *complete ); \ No newline at end of file diff --git a/src/engine/client/cin_ogm.c b/src/engine/client/cin_ogm.c new file mode 100644 index 0000000000..25fd7ab045 --- /dev/null +++ b/src/engine/client/cin_ogm.c @@ -0,0 +1,1012 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. +Copyright (C) 2008 Stefan Langer + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code?). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +/* + + This is a "ogm"-decoder to use a "better"(smaller files,higher resolutions) Cinematic-Format than roq + + In this code "ogm" is only: ogg wrapper, vorbis audio, xvid video (or theora video) + (ogm(Ogg Media) in general is ogg wrapper with all kind of audio/video/subtitle/...) + +... infos used for this src: +xvid: + * examples/xvid_decraw.c + * xvid.h +ogg/vobis: + * decoder_example.c (libvorbis src) + * libogg Documentation ( http://www.xiph.org/ogg/doc/libogg/ ) + * VLC ogg demux ( http://trac.videolan.org/vlc/browser/trunk/modules/demux/ogg.c ) +theora: + * theora doxygen docs (1.0beta1) +*/ + +#if defined(USE_CODEC_VORBIS) && (defined(USE_CIN_XVID) || defined(USE_CIN_THEORA)) + +#include +#include + +#ifdef USE_CIN_XVID +#include + +//Dushan +#if defined(WIN32) || defined(__i386__) +#define ARCH_IS_32BIT +#define ARCH_IS_IA32 +#else +#define ARCH_IS_64BIT +#define ARCH_IS_IA64 +#endif + +#endif + +#ifdef USE_CIN_THEORA +#include +#endif + +#include "client.h" +#include "snd_local.h" + +#define OGG_BUFFER_SIZE 8*1024 //4096 + +typedef struct +{ + fileHandle_t ogmFile; + + ogg_sync_state oy; /* sync and verify incoming physical bitstream */ + //ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */ + ogg_stream_state os_audio; + ogg_stream_state os_video; + + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */ + vorbis_comment vc; /* struct that stores all the bitstream user comments */ + + qboolean videoStreamIsXvid; //FIXME: atm there isn't realy a check for this (all "video" streams are handelt as xvid, because xvid support more than one "subtype") +#ifdef USE_CIN_XVID + xvid_dec_stats_t xvid_dec_stats; + void *xvid_dec_handle; +#endif + qboolean videoStreamIsTheora; +#ifdef USE_CIN_THEORA + theora_info th_info; // dump_video.c(example decoder): ti + theora_comment th_comment; // dump_video.c(example decoder): tc + theora_state th_state; // dump_video.c(example decoder): td + + yuv_buffer th_yuvbuffer; +#endif + + unsigned char *outputBuffer; + int outputWidht; + int outputHeight; + int outputBufferSize; // in Pixel (so "real Bytesize" = outputBufferSize*4) + int VFrameCount; // output video-stream + ogg_int64_t Vtime_unit; + int currentTime; // input from Run-function +} cin_ogm_t; + +static cin_ogm_t g_ogm; + +int nextNeededVFrame(void); + + +/* ####################### ####################### + + XVID + +*/ +#ifdef USE_CIN_XVID + +#define BPP 4 + +static int init_xvid() +{ + int ret; + + xvid_gbl_init_t xvid_gbl_init; + xvid_dec_create_t xvid_dec_create; + + /* Reset the structure with zeros */ + memset(&xvid_gbl_init, 0, sizeof(xvid_gbl_init_t)); + memset(&xvid_dec_create, 0, sizeof(xvid_dec_create_t)); + + /* Version */ + xvid_gbl_init.version = XVID_VERSION; + + xvid_gbl_init.cpu_flags = 0; + xvid_gbl_init.debug = 0; + + xvid_global(NULL, 0, &xvid_gbl_init, NULL); + + /* Version */ + xvid_dec_create.version = XVID_VERSION; + + /* + * Image dimensions -- set to 0, xvidcore will resize when ever it is + * needed + */ + xvid_dec_create.width = 0; + xvid_dec_create.height = 0; + + ret = xvid_decore(NULL, XVID_DEC_CREATE, &xvid_dec_create, NULL); + + g_ogm.xvid_dec_handle = xvid_dec_create.handle; + + return (ret); +} + +static int dec_xvid(unsigned char *input, int input_size) +{ + int ret; + + xvid_dec_frame_t xvid_dec_frame; + + /* Reset all structures */ + memset(&xvid_dec_frame, 0, sizeof(xvid_dec_frame_t)); + memset(&g_ogm.xvid_dec_stats, 0, sizeof(xvid_dec_stats_t)); + + /* Set version */ + xvid_dec_frame.version = XVID_VERSION; + g_ogm.xvid_dec_stats.version = XVID_VERSION; + + /* No general flags to set */ + xvid_dec_frame.general = XVID_LOWDELAY; //0; + + /* Input stream */ + xvid_dec_frame.bitstream = input; + xvid_dec_frame.length = input_size; + + /* Output frame structure */ + xvid_dec_frame.output.plane[0] = g_ogm.outputBuffer; + xvid_dec_frame.output.stride[0] = g_ogm.outputWidht * BPP; + if(g_ogm.outputBuffer == NULL) + xvid_dec_frame.output.csp = XVID_CSP_NULL; + else + xvid_dec_frame.output.csp = XVID_CSP_RGBA; // example was with XVID_CSP_I420 + + ret = xvid_decore(g_ogm.xvid_dec_handle, XVID_DEC_DECODE, &xvid_dec_frame, &g_ogm.xvid_dec_stats); + + return (ret); +} + +static int shutdown_xvid() +{ + int ret = 0; + + if(g_ogm.xvid_dec_handle) + ret = xvid_decore(g_ogm.xvid_dec_handle, XVID_DEC_DESTROY, NULL, NULL); + + return (ret); +} +#endif + +/* ####################### ####################### + + OGG/OGM + ... also calls to vorbis/theora-libs + +*/ + +/* + loadBlockToSync + + return: + !0 -> no data transferred +*/ +static int loadBlockToSync(void) +{ + int r = -1; + char *buffer; + int bytes; + + if(g_ogm.ogmFile) + { + buffer = ogg_sync_buffer(&g_ogm.oy, OGG_BUFFER_SIZE); + bytes = FS_Read(buffer, OGG_BUFFER_SIZE, g_ogm.ogmFile); + ogg_sync_wrote(&g_ogm.oy, bytes); + + r = (bytes == 0); + } + + return r; +} + +/* + loadPagesToStreams + + return: + !0 -> no data transferred (or not for all Streams) +*/ +static int loadPagesToStreams(void) +{ + int r = -1; + int AudioPages = 0; + int VideoPages = 0; + ogg_stream_state *osptr = NULL; + ogg_page og; + + while(!AudioPages || !VideoPages) + { + if(ogg_sync_pageout(&g_ogm.oy, &og) != 1) + break; + + if(g_ogm.os_audio.serialno == ogg_page_serialno(&og)) + { + osptr = &g_ogm.os_audio; + ++AudioPages; + } + if(g_ogm.os_video.serialno == ogg_page_serialno(&og)) + { + osptr = &g_ogm.os_video; + ++VideoPages; + } + + if(osptr != NULL) + { + ogg_stream_pagein(osptr, &og); + } + } + + if(AudioPages && VideoPages) + r = 0; + + return r; +} + +#define SIZEOF_RAWBUFF 4*1024 +static byte rawBuffer[SIZEOF_RAWBUFF]; + +#define MIN_AUDIO_PRELOAD 400 // in ms +#define MAX_AUDIO_PRELOAD 500 // in ms + + +/* + + return: audio wants more packets +*/ +static qboolean loadAudio(void) +{ + qboolean anyDataTransferred = qtrue; + float **pcm; + float *right, *left; + int samples, samplesNeeded; + int i; + short *ptr; + ogg_packet op; + vorbis_block vb; + + memset(&op, 0, sizeof(op)); + memset(&vb, 0, sizeof(vb)); + vorbis_block_init(&g_ogm.vd, &vb); + + while(anyDataTransferred && g_ogm.currentTime + MAX_AUDIO_PRELOAD > (int)(g_ogm.vd.granulepos * 1000 / g_ogm.vi.rate)) + { + anyDataTransferred = qfalse; + + if((samples = vorbis_synthesis_pcmout(&g_ogm.vd, &pcm)) > 0) + { + // vorbis -> raw + ptr = (short *)rawBuffer; + samplesNeeded = (SIZEOF_RAWBUFF) / (2 * 2); // (width*channel) + if(samples < samplesNeeded) + samplesNeeded = samples; + + left = pcm[0]; + right = (g_ogm.vi.channels > 1) ? pcm[1] : pcm[0]; + for(i = 0; i < samplesNeeded; ++i) + { + ptr[0] = (left[i] >= -1.0f && + left[i] <= 1.0f) ? left[i] * 32767.f : 32767 * ((left[i] > 0.0f) - (left[i] < 0.0f)); + ptr[1] = (right[i] >= -1.0f && + right[i] <= 1.0f) ? right[i] * 32767.f : 32767 * ((right[i] > 0.0f) - (right[i] < 0.0f)); + ptr += 2; //numChans; + } + + if(i > 0) + { + // tell libvorbis how many samples we actually consumed + vorbis_synthesis_read(&g_ogm.vd, i); + +// S_RawSamples( ssize, 22050, 2, 2, (byte *)sbuf, 1.0f ); + S_RawSamples(0, i, g_ogm.vi.rate, 2, 2, rawBuffer, 1.0f, 1.0f); + + anyDataTransferred = qtrue; + } + } + + if(!anyDataTransferred) + { + // op -> vorbis + if(ogg_stream_packetout(&g_ogm.os_audio, &op)) + { + if(vorbis_synthesis(&vb, &op) == 0) + vorbis_synthesis_blockin(&g_ogm.vd, &vb); + anyDataTransferred = qtrue; + } + } + } + + vorbis_block_clear(&vb); + + if(g_ogm.currentTime + MIN_AUDIO_PRELOAD > (int)(g_ogm.vd.granulepos * 1000 / g_ogm.vi.rate)) + return qtrue; + else + return qfalse; +} + +/* + + return: 1 -> loaded a new Frame ( g_ogm.outputBuffer points to the actual frame ) + 0 -> no new Frame + <0 -> error +*/ +#ifdef USE_CIN_XVID +static int loadVideoFrameXvid() +{ + int r = 0; + ogg_packet op; + int used_bytes = 0; + + memset(&op, 0, sizeof(op)); + + while(!r && (ogg_stream_packetout(&g_ogm.os_video, &op))) + { + used_bytes = dec_xvid(op.packet, op.bytes); + if(g_ogm.xvid_dec_stats.type == XVID_TYPE_VOL) + { + if(g_ogm.outputWidht != g_ogm.xvid_dec_stats.data.vol.width || + g_ogm.outputHeight != g_ogm.xvid_dec_stats.data.vol.height) + { + g_ogm.outputWidht = g_ogm.xvid_dec_stats.data.vol.width; + g_ogm.outputHeight = g_ogm.xvid_dec_stats.data.vol.height; + Com_DPrintf("[XVID]new resolution %dx%d\n", g_ogm.outputWidht, g_ogm.outputHeight); + } + + if(g_ogm.outputBufferSize < g_ogm.xvid_dec_stats.data.vol.width * g_ogm.xvid_dec_stats.data.vol.height) + { + + g_ogm.outputBufferSize = g_ogm.xvid_dec_stats.data.vol.width * g_ogm.xvid_dec_stats.data.vol.height; + + /* Free old output buffer */ + if(g_ogm.outputBuffer) + free(g_ogm.outputBuffer); + + /* Allocate the new buffer */ + g_ogm.outputBuffer = (unsigned char *)malloc(g_ogm.outputBufferSize * 4); //FIXME? should the 4 stay for BPP? + if(g_ogm.outputBuffer == NULL) + { + g_ogm.outputBufferSize = 0; + r = -2; + break; + } + } + + // use the rest of this packet + used_bytes += dec_xvid(op.packet + used_bytes, op.bytes - used_bytes); + } + + // we got a real output frame ... + if(g_ogm.xvid_dec_stats.type > 0) + { + r = 1; + + ++g_ogm.VFrameCount; +// Com_Printf("frame infos: %d %d %d\n", xvid_dec_stats.data.vop.general, xvid_dec_stats.data.vop.time_base, xvid_dec_stats.data.vop.time_increment); +// Com_Printf("frame info time: %d (Frame# %d, %d)\n", xvid_dec_stats.data.vop.time_base, VFrameCount, (int)(VFrameCount*Vtime_unit/10000000)); + } + +// if((op.bytes-used_bytes)>0) +// Com_Printf("unused: %d(firstChar: %X)\n",(op.bytes-used_bytes),(int)(op.packet[used_bytes])); + } + + return r; +} +#endif + +/* + + return: 1 -> loaded a new Frame ( g_ogm.outputBuffer points to the actual frame ) + 0 -> no new Frame + <0 -> error +*/ +#ifdef USE_CIN_THEORA +/* +how many >> are needed to make y==x (shifting y>>i) +return: -1 -> no match + >=0 -> number of shifts +*/ +static int findSizeShift(int x, int y) +{ + int i; + + for(i = 0; (y >> i); ++i) + if(x == (y >> i)) + return i; + + return -1; +} + +static int loadVideoFrameTheora(void) +{ + int r = 0; + ogg_packet op; + + memset(&op, 0, sizeof(op)); + + while(!r && (ogg_stream_packetout(&g_ogm.os_video, &op))) + { + ogg_int64_t th_frame; + + theora_decode_packetin(&g_ogm.th_state, &op); + + th_frame = theora_granule_frame(&g_ogm.th_state, g_ogm.th_state.granulepos); + + if((g_ogm.VFrameCount < th_frame && th_frame >= nextNeededVFrame()) || !g_ogm.outputBuffer) + { +// int i,j; + int yWShift, uvWShift; + int yHShift, uvHShift; + + if(theora_decode_YUVout(&g_ogm.th_state, &g_ogm.th_yuvbuffer)) + continue; + + if(g_ogm.outputWidht != g_ogm.th_info.width || g_ogm.outputHeight != g_ogm.th_info.height) + { + g_ogm.outputWidht = g_ogm.th_info.width; + g_ogm.outputHeight = g_ogm.th_info.height; + Com_DPrintf("[Theora(ogg)]new resolution %dx%d\n", g_ogm.outputWidht, g_ogm.outputHeight); + } + + if(g_ogm.outputBufferSize < g_ogm.th_info.width * g_ogm.th_info.height) + { + + g_ogm.outputBufferSize = g_ogm.th_info.width * g_ogm.th_info.height; + + /* Free old output buffer */ + if(g_ogm.outputBuffer) + free(g_ogm.outputBuffer); + + /* Allocate the new buffer */ + g_ogm.outputBuffer = (unsigned char *)malloc(g_ogm.outputBufferSize * 4); + if(g_ogm.outputBuffer == NULL) + { + g_ogm.outputBufferSize = 0; + r = -2; + break; + } + } + + yWShift = findSizeShift(g_ogm.th_yuvbuffer.y_width, g_ogm.th_info.width); + uvWShift = findSizeShift(g_ogm.th_yuvbuffer.uv_width, g_ogm.th_info.width); + yHShift = findSizeShift(g_ogm.th_yuvbuffer.y_height, g_ogm.th_info.height); + uvHShift = findSizeShift(g_ogm.th_yuvbuffer.uv_height, g_ogm.th_info.height); + + if(yWShift < 0 || uvWShift < 0 || yHShift < 0 || uvHShift < 0) + { + Com_Printf("[Theora] unexpected resolution in a yuv-Frame\n"); + r = -1; + } + else + { + + Frame_yuv_to_rgb24(g_ogm.th_yuvbuffer.y, g_ogm.th_yuvbuffer.u, g_ogm.th_yuvbuffer.v, + g_ogm.th_info.width, g_ogm.th_info.height, g_ogm.th_yuvbuffer.y_stride, + g_ogm.th_yuvbuffer.uv_stride, yWShift, uvWShift, yHShift, uvHShift, + (unsigned int *)g_ogm.outputBuffer); + +/* unsigned char* pixelPtr = g_ogm.outputBuffer; + unsigned int* pixPtr; + pixPtr = (unsigned int*)g_ogm.outputBuffer; + + //TODO: use one yuv->rgb funktion for the hole frame (the big amout of stack movement(yuv->rgb calls) couldn't be good ;) ) + for(j=0;jrgb code + *pixPtr++ = yuv_to_rgb24( g_ogm.th_yuvbuffer.y[(i>>yWShift)+(j>>yHShift)*g_ogm.th_yuvbuffer.y_stride], + g_ogm.th_yuvbuffer.u[(i>>uvWShift)+(j>>uvHShift)*g_ogm.th_yuvbuffer.uv_stride], + g_ogm.th_yuvbuffer.v[(i>>uvWShift)+(j>>uvHShift)*g_ogm.th_yuvbuffer.uv_stride]); +#endif + } + } +*/ + + r = 1; + g_ogm.VFrameCount = th_frame; + } + } + + + } + + return r; +} +#endif + + +/* + + return: 1 -> loaded a new Frame ( g_ogm.outputBuffer points to the actual frame ) + 0 -> no new Frame + <0 -> error +*/ +static int loadVideoFrame(void) +{ +#ifdef USE_CIN_XVID + if(g_ogm.videoStreamIsXvid) + return loadVideoFrameXvid(); +#endif +#ifdef USE_CIN_THEORA + if(g_ogm.videoStreamIsTheora) + return loadVideoFrameTheora(); +#endif + + // if we come to this point, there will be no codec that use the stream content ... + if(g_ogm.os_video.serialno) + { + ogg_packet op; + + while(ogg_stream_packetout(&g_ogm.os_video, &op)); + } + + return 1; +} + +/* + + return: qtrue => noDataTransferred +*/ +static qboolean loadFrame(void) +{ + qboolean anyDataTransferred = qtrue; + qboolean needVOutputData = qtrue; + +// qboolean audioSDone = qfalse; +// qboolean videoSDone = qfalse; + qboolean audioWantsMoreData = qfalse; + int status; + + while(anyDataTransferred && (needVOutputData || audioWantsMoreData)) + { + anyDataTransferred = qfalse; + +// xvid -> "gl" ? videoDone : needPacket +// vorbis -> raw sound ? audioDone : needPacket +// anyDataTransferred = videoDone && audioDone; +// needVOutputData = videoDone && audioDone; +// if needPacket + { +// videoStream -> xvid ? videoStreamDone : needPage +// audioSteam -> vorbis ? audioStreamDone : needPage +// anyDataTransferred = audioStreamDone && audioStreamDone; + + if(needVOutputData && (status = loadVideoFrame())) + { + needVOutputData = qfalse; + if(status > 0) + anyDataTransferred = qtrue; + else + anyDataTransferred = qfalse; // error (we don't need any videodata and we had no transferred) + } + +// if needPage + if(needVOutputData || audioWantsMoreData) + { + // try to transfer Pages to the audio- and video-Stream + if(loadPagesToStreams()) + { + // try to load a datablock from file + anyDataTransferred |= !loadBlockToSync(); + } + else + anyDataTransferred = qtrue; // successful loadPagesToStreams() + } + + // load all Audio after loading new pages ... + if(g_ogm.VFrameCount > 1) // wait some videoframes (it's better to have some delay, than a lagy sound) + audioWantsMoreData = loadAudio(); + } + } + +// ogg_packet_clear(&op); + + return !anyDataTransferred; +} + +//from VLC ogg.c ( http://trac.videolan.org/vlc/browser/trunk/modules/demux/ogg.c ) +typedef struct +{ + char streamtype[8]; + char subtype[4]; + + ogg_int32_t size; /* size of the structure */ + + ogg_int64_t time_unit; /* in reference time */// in 10^-7 seconds (dT between frames) + ogg_int64_t samples_per_unit; + ogg_int32_t default_len; /* in media time */ + + ogg_int32_t buffersize; + ogg_int16_t bits_per_sample; + + union + { + struct + { + ogg_int32_t width; + ogg_int32_t height; + } stream_header_video; + + struct + { + ogg_int16_t channels; + ogg_int16_t blockalign; + ogg_int32_t avgbytespersec; + } stream_header_audio; + } sh; +} stream_header_t; + +qboolean isPowerOf2(int x) +{ + int bitsSet = 0; + int i; + + for(i = 0; i < sizeof(int) * 8; ++i) + if(x & (1 << i)) + ++bitsSet; + + return (bitsSet <= 1); +} + +/* + return: 0 -> no problem +*/ +//TODO: vorbis/theora-header&init in sub-functions +//TODO: "clean" error-returns ... +int Cin_OGM_Init(const char *filename) { + int status; + ogg_page og; + ogg_packet op; + int i; + + if(g_ogm.ogmFile) + { + Com_Printf(S_COLOR_YELLOW "WARNING: it seams there was already a ogm running, it will be killed to start %s\n", filename); + Cin_OGM_Shutdown(); + } + + memset(&g_ogm, 0, sizeof(cin_ogm_t)); + + FS_FOpenFileRead(filename, &g_ogm.ogmFile, qtrue); + if(!g_ogm.ogmFile) + { + Com_Printf(S_COLOR_YELLOW "WARNING: Can't open ogm-file for reading (%s)\n", filename); + return -1; + } + + ogg_sync_init(&g_ogm.oy); /* Now we can read pages */ + + //FIXME? can serialno be 0 in ogg? (better way to check inited?) + //TODO: support for more than one audio stream? / detect files with one stream(or without correct ones) + while(!g_ogm.os_audio.serialno || !g_ogm.os_video.serialno) + { + if(ogg_sync_pageout(&g_ogm.oy, &og) == 1) + { + if(strstr((char *)(og.body + 1), "vorbis")) + { + //FIXME? better way to find audio stream + if(g_ogm.os_audio.serialno) + { + Com_Printf(S_COLOR_YELLOW "WARNING: more than one audio stream, in ogm-file(%s) ... we will stay at the first one\n", filename); + } + else + { + ogg_stream_init(&g_ogm.os_audio, ogg_page_serialno(&og)); + ogg_stream_pagein(&g_ogm.os_audio, &og); + } + } +#ifdef USE_CIN_THEORA + if(strstr((char *)(og.body + 1), "theora")) + { + if(g_ogm.os_video.serialno) + { + Com_Printf(S_COLOR_YELLOW "WARNING: more than one video stream, in ogm-file(%s) ... we will stay at the first one\n", filename); + } + else + { + g_ogm.videoStreamIsTheora = qtrue; + ogg_stream_init(&g_ogm.os_video, ogg_page_serialno(&og)); + ogg_stream_pagein(&g_ogm.os_video, &og); + } + } +#endif +#ifdef USE_CIN_XVID + if(strstr((char *)(og.body + 1), "video")) + { //FIXME? better way to find video stream + if(g_ogm.os_video.serialno) + { + Com_Printf("more than one video stream, in ogm-file(%s) ... we will stay at the first one\n", filename); + } + else + { + stream_header_t *sh; + + g_ogm.videoStreamIsXvid = qtrue; + + sh = (stream_header_t *) (og.body + 1); + //TODO: one solution for checking xvid and theora + if(!isPowerOf2(sh->sh.stream_header_video.width)) + { + Com_Printf("VideoWidth of the ogm-file isn't a power of 2 value (%s)\n", filename); + + return -5; + } + if(!isPowerOf2(sh->sh.stream_header_video.height)) + { + Com_Printf("VideoHeight of the ogm-file isn't a power of 2 value (%s)\n", filename); + + return -6; + } + + g_ogm.Vtime_unit = sh->time_unit; + + ogg_stream_init(&g_ogm.os_video, ogg_page_serialno(&og)); + ogg_stream_pagein(&g_ogm.os_video, &og); + } + } +#endif + } + else if(loadBlockToSync()) + break; + } + + if(g_ogm.videoStreamIsXvid && g_ogm.videoStreamIsTheora) + { + Com_Printf(S_COLOR_YELLOW "WARNING: Found \"video\"- and \"theora\"-stream ,ogm-file (%s)\n", filename); + return -2; + } + +#if 1 + if(!g_ogm.os_audio.serialno) + { + Com_Printf(S_COLOR_YELLOW "WARNING: Haven't found a audio(vorbis) stream in ogm-file (%s)\n", filename); + return -2; + } +#endif + if(!g_ogm.os_video.serialno) + { + Com_Printf(S_COLOR_YELLOW "WARNING: Haven't found a video stream in ogm-file (%s)\n", filename); + return -3; + } + + //load vorbis header + vorbis_info_init(&g_ogm.vi); + vorbis_comment_init(&g_ogm.vc); + i = 0; + while(i < 3) + { + status = ogg_stream_packetout(&g_ogm.os_audio, &op); + if(status < 0) + { + Com_Printf(S_COLOR_YELLOW "WARNING: Corrupt ogg packet while loading vorbis-headers, ogm-file(%s)\n", filename); + return -8; + } + if(status > 0) + { + status = vorbis_synthesis_headerin(&g_ogm.vi, &g_ogm.vc, &op); + if(i == 0 && status < 0) + { + Com_Printf(S_COLOR_YELLOW "WARNING: This Ogg bitstream does not contain Vorbis audio data, ogm-file(%s)\n", filename); + return -9; + } + ++i; + } + else if(loadPagesToStreams()) + { + if(loadBlockToSync()) + { + Com_Printf(S_COLOR_YELLOW "WARNING: Couldn't find all vorbis headers before end of ogm-file (%s)\n", filename); + return -10; + } + } + } + + vorbis_synthesis_init(&g_ogm.vd, &g_ogm.vi); + +#ifdef USE_CIN_XVID + status = init_xvid(); + if(status) + { + Com_Printf("[Xvid]Decore INIT problem, return value %d(ogm-file: %s)\n", status, filename); + + return -4; + } +#endif + +#ifdef USE_CIN_THEORA + if(g_ogm.videoStreamIsTheora) + { + ROQ_GenYUVTables(); + + theora_info_init(&g_ogm.th_info); + theora_comment_init(&g_ogm.th_comment); + + i = 0; + while(i < 3) + { + status = ogg_stream_packetout(&g_ogm.os_video, &op); + if(status < 0) + { + Com_Printf(S_COLOR_YELLOW "WARNING: Corrupt ogg packet while loading theora-headers, ogm-file(%s)\n", filename); + return -8; + } + if(status > 0) + { + status = theora_decode_header(&g_ogm.th_info, &g_ogm.th_comment, &op); + if(i == 0 && status != 0) + { + Com_Printf(S_COLOR_YELLOW "WARNING: This Ogg bitstream does not contain theora data, ogm-file(%s)\n", filename); + return -9; + } + ++i; + } + else if(loadPagesToStreams()) + { + if(loadBlockToSync()) + { + Com_Printf(S_COLOR_YELLOW "WARNING: Couldn't find all theora headers before end of ogm-file (%s)\n", filename); + return -10; + } + } + } + + theora_decode_init(&g_ogm.th_state, &g_ogm.th_info); + + if(!isPowerOf2(g_ogm.th_info.width)) + { + Com_Printf(S_COLOR_YELLOW "WARNING: VideoWidth of the ogm-file isn't a power of 2 value (%s)\n", filename); + return -5; + } + if(!isPowerOf2(g_ogm.th_info.height)) + { + Com_Printf(S_COLOR_YELLOW "WARNING: VideoHeight of the ogm-file isn't a power of 2 value (%s)\n", filename); + return -6; + } + + g_ogm.Vtime_unit = ((ogg_int64_t) g_ogm.th_info.fps_denominator * 1000 * 10000 / g_ogm.th_info.fps_numerator); + } +#endif + + Com_DPrintf("OGM-Init done (%s)\n", filename); + + return 0; +} + +int nextNeededVFrame(void) +{ + return (int)(g_ogm.currentTime * (ogg_int64_t) 10000 / g_ogm.Vtime_unit); +} + +/* + + time ~> time in ms to which the movie should run + return: 0 => nothing special + 1 => eof +*/ +int Cin_OGM_Run(int time) +{ + + g_ogm.currentTime = time; + + while(!g_ogm.VFrameCount || time + 20 >= (int)(g_ogm.VFrameCount * g_ogm.Vtime_unit / 10000)) + { + if(loadFrame()) + return 1; + } + + return 0; +} + +/* + Gives a Pointer to the current Output-Buffer + and the Resolution +*/ +unsigned char *Cin_OGM_GetOutput(int *outWidth, int *outHeight) +{ + if(outWidth != NULL) + *outWidth = g_ogm.outputWidht; + if(outHeight != NULL) + *outHeight = g_ogm.outputHeight; + + return g_ogm.outputBuffer; +} + +void Cin_OGM_Shutdown() +{ +#ifdef USE_CIN_XVID + int status; + + status = shutdown_xvid(); + if(status) + Com_Printf("[Xvid]Decore RELEASE problem, return value %d\n", status); +#endif + +#ifdef USE_CIN_THEORA + theora_clear(&g_ogm.th_state); + theora_comment_clear(&g_ogm.th_comment); + theora_info_clear(&g_ogm.th_info); +#endif + + if(g_ogm.outputBuffer) + free(g_ogm.outputBuffer); + g_ogm.outputBuffer = NULL; + + vorbis_dsp_clear(&g_ogm.vd); + vorbis_comment_clear(&g_ogm.vc); + vorbis_info_clear(&g_ogm.vi); /* must be called last (comment from vorbis example code) */ + + ogg_stream_clear(&g_ogm.os_audio); + ogg_stream_clear(&g_ogm.os_video); + + ogg_sync_clear(&g_ogm.oy); + + FS_FCloseFile(g_ogm.ogmFile); + g_ogm.ogmFile = 0; +} + +#else +int Cin_OGM_Init(const char *filename) +{ + return 1; +} +int Cin_OGM_Run(int time) +{ + return 1; +} +unsigned char *Cin_OGM_GetOutput(int *outWidth, int *outHeight) +{ + return 0; +} + +void Cin_OGM_Shutdown() +{ +} +#endif + diff --git a/src/engine/client/cl_avi.c b/src/engine/client/cl_avi.c new file mode 100644 index 0000000000..beaa17169f --- /dev/null +++ b/src/engine/client/cl_avi.c @@ -0,0 +1,659 @@ +/* +=========================================================================== +Copyright (C) 2005-2006 Tim Angus + +This file is part of OpenWolf source code. + +OpenWolf 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. + +OpenWolf 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 OpenWolf 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; + + // Buffers only need to store RGB pixels. + // Allocate a bit more space for the capture buffer to account for possible + // padding at the end of pixel lines, and padding for alignment + #define MAX_PACK_LEN 16 + afd.cBuffer = Z_Malloc((afd.width * 3 + MAX_PACK_LEN - 1) * afd.height + MAX_PACK_LEN - 1); + // raw avi files have pixel lines start on 4-byte boundaries + afd.eBuffer = Z_Malloc(PAD(afd.width * 3, AVI_LINE_PADDING) * afd.height); + + 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) + afd.audio = qtrue; + else + afd.audio = qfalse; //FIXME: audio not implemented for this case + } + 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(pcmBuffer, 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/src/engine/client/cl_cgame.c b/src/engine/client/cl_cgame.c new file mode 100644 index 0000000000..a7d0a5d6b0 --- /dev/null +++ b/src/engine/client/cl_cgame.c @@ -0,0 +1,1848 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +// cl_cgame.c -- client system interaction with client game + +#ifdef _MSC_VER +#include "../../libs/msinttypes/inttypes.h" +#else +#include +#endif + +#include "client.h" + +#include "../botlib/botlib.h" + +#include "libmumblelink.h" + +extern botlib_export_t *botlib_export; + +extern qboolean loadCamera(int camNum, const char *name); +extern void startCamera(int camNum, int time); +extern qboolean getCameraInfo(int camNum, int time, vec3_t * origin, vec3_t * angles, float *fov); + +static void(*completer)(const char *s) = NULL; + +// NERVE - SMF +void Key_GetBindingBuf(int keynum, char *buf, int buflen); +void Key_KeynumToStringBuf(int keynum, char *buf, int buflen); + +// -NERVE - SMF + +// ydnar: can we put this in a header, pls? +void Key_GetBindingByString(const char *binding, int *key1, int *key2); + + +/* +==================== +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; + 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, int flags, float sensitivityScale, int mpIdentClient) +{ + cl.cgameUserCmdValue = userCmdValue; + cl.cgameFlags = flags; + cl.cgameSensitivity = sensitivityScale; + cl.cgameMpIdentClient = mpIdentClient; // NERVE - SMF +} + +/* +================== +CL_SetClientLerpOrigin +================== +*/ +void CL_SetClientLerpOrigin(float x, float y, float z) +{ + cl.cgameClientLerpOrigin[0] = x; + cl.cgameClientLerpOrigin[1] = y; + cl.cgameClientLerpOrigin[2] = z; +} + +/* +===================== +CL_CompleteCgameCommand +===================== +*/ +void CL_CompleteCgameCommand( char *args, int argNum ) { + Field_CompleteCgame( argNum ); +} + +/* +===================== +CL_CgameCompletion +===================== +*/ +void CL_CgameCompletion( void(*callback)(const char *s), int argNum ) { + completer = callback; + VM_Call( cgvm, CG_COMPLETE_COMMAND, argNum ); + completer = NULL; +} + +/* +============== +CL_AddCgameCommand +============== +*/ +void CL_AddCgameCommand(const char *cmdName) +{ + Cmd_AddCommand(cmdName, NULL); + Cmd_SetCommandCompletionFunc( cmdName, CL_CompleteCgameCommand ); +} + +/* +============== +CL_CgameError +============== +*/ +void CL_CgameError(const char *string) +{ + Com_Error(ERR_DROP, "%s", string); +} + +qboolean CL_CGameCheckKeyExec(int key) +{ + if(cgvm) + { + return VM_Call(cgvm, CG_CHECKEXECKEY, key); + } + else + { + return qfalse; + } +} + + +/* +===================== +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"); + } +// s = Cmd_Argv(2); + // 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; + + 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; + 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; + + if(cl_showServerCommands->integer) + { // NERVE - SMF + Com_DPrintf("serverCommand: %i : %s\n", serverCommandNumber, s); + } + + rescan: + Cmd_TokenizeString(s); + cmd = Cmd_Argv(0); + argc = Cmd_Argc(); + + if(!strcmp(cmd, "disconnect")) + { + // NERVE - SMF - 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(); + memset(cl.cmds, 0, sizeof(cl.cmds)); + return qtrue; + } + + if(!strcmp(cmd, "popup")) + { // direct server to client popup request, bypassing cgame +// trap_UI_Popup(Cmd_Argv(1)); +// if ( cls.state == CA_ACTIVE && !clc.demoplaying ) { +// VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_CLIPBOARD); +// Menus_OpenByName(Cmd_Argv(1)); +// } + return qfalse; + } + +#ifdef USE_CRYPTO + if ( cl_pubkeyID->integer && !strcmp( cmd, "pubkey_request" ) ) { + char buffer[ MAX_STRING_CHARS ] = "pubkey "; + mpz_get_str( buffer + 7, 16, public_key.n ); + CL_AddReliableCommand( buffer ); + return qfalse; + } + + if ( cl_pubkeyID->integer && !strcmp( cmd, "pubkey_decrypt" ) && argc > 1 ) { + char buffer[ MAX_STRING_CHARS ] = "pubkey_identify "; + unsigned int msg_len = MAX_STRING_CHARS - 16; + mpz_t message; + mpz_init_set_str( message, Cmd_Argv( 1 ), 16 ); + if ( rsa_decrypt( &private_key, &msg_len, (unsigned char *) buffer + 16, message ) ) + { + nettle_mpz_set_str_256_u( message, msg_len, (unsigned char *) buffer + 16 ); + mpz_get_str( buffer + 16, 16, message ); + CL_AddReliableCommand( buffer ); + } + mpz_clear( message ); + return qfalse; + } +#endif + + // we may want to put a "connect to other server" command here + + // cgame can now act on the command + return qtrue; +} + +// DHM - Nerve :: Copied from server to here +/* +==================== +CL_SetExpectedHunkUsage + + Sets com_expectedhunkusage, so the client knows how to draw the percentage bar +==================== +*/ +void CL_SetExpectedHunkUsage(const char *mapname) +{ + int handle; + char *memlistfile = "hunkusage.dat"; + char *buf; + char *buftrav; + char *token; + int len; + + len = FS_FOpenFileByMode(memlistfile, &handle, FS_READ); + if(len >= 0) + { // the file exists, so read it in, strip out the current entry for this map, and save it out, so we can append the new value + + buf = (char *)Z_Malloc(len + 1); + memset(buf, 0, len + 1); + + FS_Read((void *)buf, len, handle); + FS_FCloseFile(handle); + + // now parse the file, filtering out the current map + buftrav = buf; + while((token = COM_Parse(&buftrav)) != NULL && token[0]) + { + if(!Q_stricmp(token, (char *)mapname)) + { + // found a match + token = COM_Parse(&buftrav); // read the size + if(token && *token) + { + // this is the usage + com_expectedhunkusage = atoi(token); + Z_Free(buf); + return; + } + } + } + + Z_Free(buf); + } + // just set it to a negative number,so the cgame knows not to draw the percent bar + com_expectedhunkusage = -1; +} + +// dhm - nerve + +/* +==================== +CL_SendBinaryMessage +==================== +*/ +static void CL_SendBinaryMessage(const char *buf, int buflen) +{ + if(buflen < 0 || buflen > MAX_BINARY_MESSAGE) + { + Com_Error(ERR_DROP, "CL_SendBinaryMessage: bad length %i", buflen); + clc.binaryMessageLength = 0; + return; + } + + clc.binaryMessageLength = buflen; + memcpy(clc.binaryMessage, buf, buflen); +} + +/* +==================== +CL_BinaryMessageStatus +==================== +*/ +static int CL_BinaryMessageStatus(void) +{ + if(clc.binaryMessageLength == 0) + { + return MESSAGE_EMPTY; + } + + if(clc.binaryMessageOverflowed) + { + return MESSAGE_WAITING_OVERFLOW; + } + + return MESSAGE_WAITING; +} + +/* +==================== +CL_CGameBinaryMessageReceived +==================== +*/ +void CL_CGameBinaryMessageReceived(const char *buf, int buflen, int serverTime) +{ + VM_Call(cgvm, CG_MESSAGERECEIVED, buf, buflen, serverTime); +} + +/* +==================== +CL_CM_LoadMap + +Just adds default parameters that cgame doesn't need to know about +==================== +*/ +void CL_CM_LoadMap(const char *mapname) +{ + int checksum; + + // DHM - Nerve :: If we are not running the server, then set expected usage here + if(!com_sv_running->integer) + { + CL_SetExpectedHunkUsage(mapname); + } + else + { + // TTimo + // catch here when a local server is started to avoid outdated com_errorDiagnoseIP + Cvar_Set("com_errorDiagnoseIP", ""); + } + + CM_LoadMap(mapname, qtrue, &checksum); +} + +/* +==================== +CL_ShutdonwCGame + +==================== +*/ +void CL_ShutdownCGame(void) +{ + cls.keyCatchers &= ~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; +} + +//static int numtraces = 0; + +/* +==================== +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", (char *)VMA(1)); + return 0; + case CG_ERROR: + Com_Error(ERR_DROP, "%s", (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_CVAR_LATCHEDVARIABLESTRINGBUFFER: + Cvar_LatchedVariableStringBuffer(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_LITERAL_ARGS: + // Dushan : FIX ME + Cmd_LiteralArgsBuffer( VMA(1), args[2] ); +// Cmd_ArgsBuffer(VMA(1), args[2]); + return 0; + case CG_GETDEMOSTATE: + //return CL_DemoState( ); + // Dushan : FIX ME + return 0; + case CG_GETDEMOPOS: + //return CL_DemoPos( ); + 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: + return FS_Write(VMA(1), args[2], args[3]); + case CG_FS_FCLOSEFILE: + FS_FCloseFile(args[1]); + return 0; + case CG_FS_GETFILELIST: + return FS_GetFileList(VMA(1), VMA(2), VMA(3), args[4]); + case CG_FS_DELETEFILE: + return FS_Delete(VMA(1)); + 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_COMPLETE_CALLBACK: + if( completer ) + completer( VMA(1) ); + return 0; + case CG_SENDCLIENTCOMMAND: + CL_AddReliableCommand(VMA(1)); + return 0; + case CG_UPDATESCREEN: + 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), qfalse); + case CG_CM_TEMPCAPSULEMODEL: + return CM_TempBoxModel(VMA(1), VMA(2), 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: +// numtraces++; + //CM_BoxTrace(VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], /*int capsule */ qfalse); + CM_BoxTrace(VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], TT_AABB); + return 0; + case CG_CM_TRANSFORMEDBOXTRACE: +// numtraces++; + //CM_TransformedBoxTrace(VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], VMA(8), VMA(9), /*int capsule */ qfalse); + CM_TransformedBoxTrace(VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], VMA(8), VMA(9), TT_AABB); + return 0; + case CG_CM_CAPSULETRACE: +// numtraces++; + //CM_BoxTrace(VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], /*int capsule */ qtrue); + CM_BoxTrace(VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], TT_CAPSULE); + return 0; + case CG_CM_TRANSFORMEDCAPSULETRACE: +// numtraces++; + //CM_TransformedBoxTrace(VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], VMA(8), VMA(9), /*int capsule */ qtrue); + CM_TransformedBoxTrace(VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], VMA(8), VMA(9), TT_CAPSULE); + return 0; + case CG_CM_BISPHERETRACE: + CM_BiSphereTrace( VMA(1), VMA(2), VMA(3), VMF(4), VMF(5), args[6], args[7] ); + return 0; + case CG_CM_TRANSFORMEDBISPHERETRACE: + CM_TransformedBiSphereTrace( VMA(1), VMA(2), VMA(3), VMF(4), VMF(5), args[6], args[7], VMA(8) ); + 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_R_PROJECTDECAL: + re.ProjectDecal(args[1], args[2], VMA(3), VMA(4), VMA(5), args[6], args[7]); + return 0; + case CG_R_CLEARDECALS: + re.ClearDecals(); + return 0; + + case CG_S_STARTSOUND: + S_StartSound( VMA(1), args[2], args[3], args[4] ); + //S_StartSound(VMA(1), args[2], args[3], args[4], args[5]); + return 0; +//----(SA) added + case CG_S_STARTSOUNDEX: + // Dushan - FIX ME + //S_StartSoundEx(VMA(1), args[2], args[3], args[4], args[5], args[6]); + return 0; +//----(SA) end + case CG_S_STARTLOCALSOUND: + //S_StartLocalSound(args[1], args[2], args[3]); + S_StartLocalSound( args[1], args[2] ); + return 0; + case CG_S_CLEARLOOPINGSOUNDS: + //S_ClearLoopingSounds(); + S_ClearLoopingSounds(args[1]); + return 0; + case CG_S_CLEARSOUNDS: + // Dushan - FIX ME + /*if(args[1] == 0) + { + S_ClearSounds(qtrue, qfalse); + } + else if(args[1] == 1) + { + S_ClearSounds(qtrue, qtrue); + }*/ + return 0; + case CG_S_ADDLOOPINGSOUND: + // FIXME MrE: handling of looping sounds changed + //S_AddLoopingSound(VMA(1), VMA(2), args[3], args[4], args[5], args[6]); + S_AddLoopingSound( args[1], VMA(2), VMA(3), args[4] ); + return 0; + case CG_S_ADDREALLOOPINGSOUND: + //S_AddRealLoopingSound(VMA(1), VMA(2), args[3], args[4], args[5], args[6]); + 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_STOPSTREAMINGSOUND: + // Dushan - FIX ME + //S_StopEntStreamingSound(args[1]); + return 0; + case CG_S_UPDATEENTITYPOSITION: + S_UpdateEntityPosition(args[1], VMA(2)); + return 0; +// Ridah, talking animations + case CG_S_GETVOICEAMPLITUDE: + // Dushan - FIX ME + //return S_GetVoiceAmplitude(args[1]); + return 0; +// done. + case CG_S_GETSOUNDLENGTH: + // Dushan - FIX ME + //return S_GetSoundLength(args[1]); + return 0; + + // ydnar: for looped sound starts + case CG_S_GETCURRENTSOUNDTIME: + // Dushan - FIX ME + //return S_GetCurrentSoundTime(); + return 0; + + case CG_S_RESPATIALIZE: + S_Respatialize(args[1], VMA(2), VMA(3), args[4]); + return 0; + case CG_S_REGISTERSOUND: +#ifdef DOOMSOUND ///// (SA) DOOMSOUND + return S_RegisterSound(VMA(1)); +#else + return S_RegisterSound(VMA(1), args[2]); +#endif ///// (SA) DOOMSOUND + case CG_S_STARTBACKGROUNDTRACK: + //S_StartBackgroundTrack(VMA(1), VMA(2), args[3]); //----(SA) added fadeup time + S_StartBackgroundTrack(VMA(1), VMA(2)); + return 0; + case CG_S_FADESTREAMINGSOUND: + // Dushan - FIX ME + //S_FadeStreamingSound(VMF(1), args[2], args[3]); //----(SA) added music/all-streaming options + return 0; + case CG_S_STARTSTREAMINGSOUND: + // Dushan - FIX ME + //return S_StartStreamingSound(VMA(1), VMA(2), args[3], args[4], args[5]); + return 0; + case CG_R_LOADWORLDMAP: + re.LoadWorld(VMA(1)); + return 0; + case CG_R_REGISTERMODEL: +#ifdef IPHONE + GLimp_AcquireGL(); + return re.RegisterModel( VMA(1) ); + GLimp_ReleaseGL(); +#else + return re.RegisterModel(VMA(1)); +#endif // IPHONE + case CG_R_REGISTERSKIN: + return re.RegisterSkin(VMA(1)); + + //----(SA) added + case CG_R_GETSKINMODEL: + return re.GetSkinModel(args[1], VMA(2), VMA(3)); + case CG_R_GETMODELSHADER: + return re.GetShaderFromModel(args[1], args[2], args[3]); + //----(SA) end + + case CG_R_REGISTERSHADER: +#ifdef IPHONE_NOTYET + GLimp_AcquireGL(); + return re.RegisterShader( VMA(1) ); + GLimp_ReleaseGL(); +#else + return re.RegisterShader(VMA(1)); +#endif // IPHONE + case CG_R_REGISTERFONT: + re.RegisterFont(VMA(1), args[2], VMA(3)); + return 0; + case CG_R_REGISTERSHADERNOMIP: +#ifdef IPHONE_NOTYET + GLimp_AcquireGL(); + return re.RegisterShaderNoMip( VMA(1) ); + GLimp_ReleaseGL(); +#else + return re.RegisterShaderNoMip(VMA(1)); +#endif // IPHONE +#if defined(USE_REFLIGHT) + case CG_R_REGISTERSHADERLIGHTATTENUATION: + return re.RegisterShaderLightAttenuation(VMA(1)); +#endif + case CG_R_CLEARSCENE: + re.ClearScene(); + return 0; + case CG_R_ADDREFENTITYTOSCENE: + re.AddRefEntityToScene(VMA(1)); + return 0; +#if defined(USE_REFLIGHT) + case CG_R_ADDREFLIGHTSTOSCENE: + re.AddRefLightToScene(VMA(1)); + return 0; +#endif + case CG_R_ADDPOLYTOSCENE: + re.AddPolyToScene(args[1], args[2], VMA(3)); + return 0; + // Ridah + case CG_R_ADDPOLYSTOSCENE: + re.AddPolysToScene(args[1], args[2], VMA(3), args[4]); + return 0; + case CG_R_ADDPOLYBUFFERTOSCENE: + re.AddPolyBufferToScene(VMA(1)); + break; + // done. +// case CG_R_LIGHTFORPOINT: +// return re.LightForPoint( VMA(1), VMA(2), VMA(3), VMA(4) ); + case CG_R_ADDLIGHTTOSCENE: + // ydnar: new dlight code + //% re.AddLightToScene( VMA(1), VMF(2), VMF(3), VMF(4), VMF(5), args[6] ); + re.AddLightToScene(VMA(1), VMF(2), VMF(3), VMF(4), VMF(5), VMF(6), args[7], args[8]); + return 0; +// case CG_R_ADDADDITIVELIGHTTOSCENE: +// re.AddAdditiveLightToScene( 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_FS_SEEK: + return FS_Seek( args[1], args[2], args[3] ); + case CG_R_ADDCORONATOSCENE: + re.AddCoronaToScene(VMA(1), VMF(2), VMF(3), VMF(4), VMF(5), args[6], args[7]); + return 0; + case CG_R_SETFOG: + re.SetFog(args[1], args[2], args[3], VMF(4), VMF(5), VMF(6), VMF(7)); + return 0; + case CG_R_SETGLOBALFOG: + re.SetGlobalFog(args[1], args[2], VMF(3), VMF(4), VMF(5), VMF(6)); + return 0; + case CG_R_RENDERSCENE: + re.RenderScene(VMA(1)); + return 0; + case CG_R_SAVEVIEWPARMS: + re.SaveViewParms(); + return 0; + case CG_R_RESTOREVIEWPARMS: + re.RestoreViewParms(); + return 0; + case CG_R_SETCOLOR: + re.SetColor(VMA(1)); + return 0; + // Tremulous + case CG_R_SETCLIPREGION: + re.SetClipRegion( 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_DRAWROTATEDPIC: + re.DrawRotatedPic(VMF(1), VMF(2), VMF(3), VMF(4), VMF(5), VMF(6), VMF(7), VMF(8), args[9], VMF(10)); + return 0; + case CG_R_DRAWSTRETCHPIC_GRADIENT: + re.DrawStretchPicGradient(VMF(1), VMF(2), VMF(3), VMF(4), VMF(5), VMF(6), VMF(7), VMF(8), args[9], VMA(10), args[11]); + return 0; + case CG_R_DRAW2DPOLYS: + re.Add2dPolys(VMA(1), args[2], args[3]); + 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), VMA(2), VMA(3), args[4]); + + 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], args[2], VMF(3), args[4]); + return 0; + case CG_SETCLIENTLERPORIGIN: + CL_SetClientLerpOrigin(VMF(1), VMF(2), VMF(3)); + 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: + Key_SetCatcher(args[1]); + return 0; + case CG_KEY_GETKEY: + return Key_GetKey(VMA(1)); + + case CG_KEY_GETOVERSTRIKEMODE: + return Key_GetOverstrikeMode(); + case CG_KEY_SETOVERSTRIKEMODE: + Key_SetOverstrikeMode(args[1]); + return 0; + + + + + case CG_MEMSET: + // we cannot return server-address to QVM ! + memset(VMA(1), args[2], args[3]); + return args[1]; + case CG_MEMCPY: + // we cannot return server-address to QVM ! + memcpy(VMA(1), VMA(2), args[3]); + return args[1]; + case CG_STRNCPY: + // we cannot return server-address to QVM ! + 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_PC_UNREAD_TOKEN: + botlib_export->PC_UnreadLastTokenHandle(args[1]); + return 0; + + case CG_S_STOPBACKGROUNDTRACK: + S_StopBackgroundTrack(); + return 0; + + case CG_REAL_TIME: + return Com_RealTime(VMA(1)); + case CG_SNAPVECTOR: + Q_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_TESTPRINTINT: + Com_Printf("%s%"PRIiPTR"\n", (char *)VMA(1), args[2]); + return 0; + case CG_TESTPRINTFLOAT: + Com_Printf("%s%f\n", (char *)VMA(1), VMF(2)); + return 0; + + case CG_LOADCAMERA: + //return loadCamera(args[1], VMA(2)); + return 0; + + case CG_STARTCAMERA: + //if(args[1] == 0) + //{ // CAM_PRIMARY + // cl.cameraMode = qtrue; + //} + //startCamera(args[1], args[2]); + return 0; + + case CG_STOPCAMERA: + //if(args[1] == 0) + //{ // CAM_PRIMARY + // cl.cameraMode = qfalse; + //} + return 0; + + case CG_GETCAMERAINFO: + //return getCameraInfo(args[1], args[2], VMA(3), VMA(4), VMA(5)); + return 0; + + case CG_GET_ENTITY_TOKEN: + return re.GetEntityToken(VMA(1), args[2]); + + case CG_INGAME_POPUP: + if(cls.state == CA_ACTIVE && !clc.demoplaying) + { + if(uivm) + { // Gordon: can be called as the system is shutting down + VM_Call(uivm, UI_SET_ACTIVE_MENU, args[1]); + } + } + return 0; + + case CG_INGAME_CLOSEPOPUP: + return 0; + + case CG_KEY_GETBINDINGBUF: + Key_GetBindingBuf(args[1], VMA(2), args[3]); + return 0; + + case CG_KEY_SETBINDING: + Key_SetBinding(args[1], VMA(2)); + return 0; + + // Dushan - Tremulous + case CG_PARSE_ADD_GLOBAL_DEFINE: + return Parse_AddGlobalDefine( VMA(1) ); + case CG_PARSE_LOAD_SOURCE: + return Parse_LoadSourceHandle( VMA(1) ); + case CG_PARSE_FREE_SOURCE: + return Parse_FreeSourceHandle( args[1] ); + case CG_PARSE_READ_TOKEN: + return Parse_ReadTokenHandle( args[1], VMA(2) ); + case CG_PARSE_SOURCE_FILE_AND_LINE: + return Parse_SourceFileAndLine( args[1], VMA(2), VMA(3) ); + + case CG_KEY_KEYNUMTOSTRINGBUF: + Key_KeynumToStringBuf(args[1], VMA(2), args[3]); + return 0; + + case CG_KEY_BINDINGTOKEYS: + Key_GetBindingByString(VMA(1), VMA(2), VMA(3)); + return 0; + + case CG_TRANSLATE_STRING: + CL_TranslateString(VMA(1), VMA(2)); + return 0; + + case CG_S_FADEALLSOUNDS: + // Dushan - FIX ME + //S_FadeAllSounds(VMF(1), args[2], args[3]); + return 0; + + case CG_R_INPVS: + return re.inPVS(VMA(1), VMA(2)); + + case CG_GETHUNKDATA: + Com_GetHunkInfo(VMA(1), VMA(2)); + return 0; + + case CG_PUMPEVENTLOOP: +// Com_EventLoop(); +// CL_WritePacket(); + return 0; + + //zinx - binary channel + case CG_SENDMESSAGE: + CL_SendBinaryMessage(VMA(1), args[2]); + return 0; + case CG_MESSAGESTATUS: + return CL_BinaryMessageStatus(); + //bani - dynamic shaders + case CG_R_LOADDYNAMICSHADER: + return re.LoadDynamicShader(VMA(1), VMA(2)); + // fretn - render to texture + case CG_R_RENDERTOTEXTURE: + re.RenderToTexture(args[1], args[2], args[3], args[4], args[5]); + return 0; + //bani + case CG_R_GETTEXTUREID: + return re.GetTextureId(VMA(1)); + //bani - flush gl rendering buffers + case CG_R_FINISH: + re.Finish(); + return 0; + case CG_GETDEMONAME: + // Dushan: FIX ME + //CL_DemoName( VMA(1), args[2] ); + return 0; + case CG_R_LIGHTFORPOINT: + return re.LightForPoint( VMA(1), VMA(2), VMA(3), VMA(4) ); + case CG_S_SOUNDDURATION: + return S_SoundDuration( args[1] ); +#if defined(USE_REFENTITY_ANIMATIONSYSTEM) + case CG_R_REGISTERANIMATION: + return re.RegisterAnimation( VMA(1) ); + case CG_R_CHECKSKELETON: + return re.CheckSkeleton(VMA(1), args[2], args[3]); + case CG_R_BUILDSKELETON: + return re.BuildSkeleton(VMA(1), args[2], args[3], args[4], VMF(5), args[6]); + case CG_R_BLENDSKELETON: + return re.BlendSkeleton(VMA(1), VMA(2), VMF(3)); + case CG_R_BONEINDEX: + return re.BoneIndex(args[1], VMA(2)); + case CG_R_ANIMNUMFRAMES: + return re.AnimNumFrames(args[1]); + case CG_R_ANIMFRAMERATE: + return re.AnimFrameRate(args[1]); +#endif + default: + Com_Error( ERR_DROP, "Bad cgame system trap: %ld", (long int) args[0] ); + } + return 0; +} + +/* +==================== +CL_UpdateLevelHunkUsage + + This updates the "hunkusage.dat" file with the current map and it's hunk usage count + + This is used for level loading, so we can show a percentage bar dependant on the amount + of hunk memory allocated so far + + This will be slightly inaccurate if some settings like sound quality are changed, but these + things should only account for a small variation (hopefully) +==================== +*/ +void CL_UpdateLevelHunkUsage(void) +{ + int handle; + char *memlistfile = "hunkusage.dat"; + char *buf, *outbuf; + char *buftrav, *outbuftrav; + char *token; + char outstr[256]; + int len, memusage; + + memusage = Cvar_VariableIntegerValue("com_hunkused") + Cvar_VariableIntegerValue("hunk_soundadjust"); + + len = FS_FOpenFileByMode(memlistfile, &handle, FS_READ); + if(len >= 0) + { // the file exists, so read it in, strip out the current entry for this map, and save it out, so we can append the new value + + buf = (char *)Z_Malloc(len + 1); + memset(buf, 0, len + 1); + outbuf = (char *)Z_Malloc(len + 1); + memset(outbuf, 0, len + 1); + + FS_Read((void *)buf, len, handle); + FS_FCloseFile(handle); + + // now parse the file, filtering out the current map + buftrav = buf; + outbuftrav = outbuf; + outbuftrav[0] = '\0'; + while((token = COM_Parse(&buftrav)) != NULL && token[0]) + { + if(!Q_stricmp(token, cl.mapname)) + { + // found a match + token = COM_Parse(&buftrav); // read the size + if(token && token[0]) + { + if(atoi(token) == memusage) + { // if it is the same, abort this process + Z_Free(buf); + Z_Free(outbuf); + return; + } + } + } + else + { // send it to the outbuf + Q_strcat(outbuftrav, len + 1, token); + Q_strcat(outbuftrav, len + 1, " "); + token = COM_Parse(&buftrav); // read the size + if(token && token[0]) + { + Q_strcat(outbuftrav, len + 1, token); + Q_strcat(outbuftrav, len + 1, "\n"); + } + else + { + Com_Error(ERR_DROP, "hunkusage.dat file is corrupt\n"); + } + } + } + + handle = FS_FOpenFileWrite(memlistfile); + if(handle < 0) + { + Com_Error(ERR_DROP, "cannot create %s\n", memlistfile); + } + // input file is parsed, now output to the new file + len = strlen(outbuf); + if(FS_Write((void *)outbuf, len, handle) != len) + { + Com_Error(ERR_DROP, "cannot write to %s\n", memlistfile); + } + FS_FCloseFile(handle); + + Z_Free(buf); + Z_Free(outbuf); + } + // now append the current map to the current file + FS_FOpenFileByMode(memlistfile, &handle, FS_APPEND); + if(handle < 0) + { + Com_Error(ERR_DROP, "cannot write to hunkusage.dat, check disk full\n"); + } + Com_sprintf(outstr, sizeof(outstr), "%s %i\n", cl.mapname, memusage); + FS_Write(outstr, strlen(outstr), handle); + FS_FCloseFile(handle); + + // now just open it and close it, so it gets copied to the pak dir + len = FS_FOpenFileByMode(memlistfile, &handle, FS_READ); + if(len >= 0) + { + FS_FCloseFile(handle); + } +} + +/* +==================== +CL_InitCGame + +Should only by called by CL_StartHunkUsers +==================== +*/ +void CL_InitCGame(void) { + const char *info; + const char *mapname; + int t1, t2; + + 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); + + // Dushan - load the dll or bytecode + cgvm = VM_Create( "cgame", CL_CgameSystemCalls, Cvar_VariableValue( "vm_cgame" ) ); + 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 + //bani - added clc.demoplaying, since some mods need this at init time, and drawactiveframe is too late for them + VM_Call(cgvm, CG_INIT, clc.serverMessageSequence, clc.lastExecutedServerCommand, clc.clientNum, clc.demoplaying); + + // 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(); + + // Ridah, update the memory usage file + CL_UpdateLevelHunkUsage(); + +// if( cl_autorecord->integer ) { +// Cvar_Set( "g_synchronousClients", "1" ); +// } +} + + +/* +==================== +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_GameCommand + +See if the current console command is claimed by the cgame +==================== +*/ +qboolean CL_GameConsoleText(void) +{ + if(!cgvm) + { + return qfalse; + } + + return VM_Call(cgvm, CG_CONSOLE_TEXT); +} + + +/* +===================== +CL_CGameRendering +===================== +*/ +void CL_CGameRendering(stereoFrame_t stereo) +{ +/* static int x = 0; + if(!((++x) % 20)) { + Com_Printf( "numtraces: %i\n", numtraces / 20 ); + numtraces = 0; + } else { + }*/ + + 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; + +#ifdef IPHONE + // Force the device into right landscape mode: + GLimp_SetMode(90); +#endif // IPHONE + + // 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); + Cbuf_AddText("\n"); + 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", "spatial"); + Com_Memset(clc.voipTargets, ~0, sizeof(clc.voipTargets)); + } +#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_paused->integer && com_sv_running->integer) + { + // paused + return; + } + + if(cl.snap.serverTime < cl.oldFrameServerTime) + { + // Ridah, if this is a localhost, then we are probably loading a savegame + if(!Q_stricmp(cls.servername, "localhost")) + { + // do nothing? + CL_FirstSnapshot(); + } + else + { + 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) + { + if(!clc.timeDemoStart) + { + clc.timeDemoStart = Sys_Milliseconds(); + } + 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) + { + Cvar_Set("timescale", "1"); + return; // end of demo + } + } + +} + +/* +==================== +CL_GetTag +==================== +*/ +qboolean CL_GetTag(int clientNum, char *tagname, orientation_t * or) +{ + if(!cgvm) + { + return qfalse; + } + + return VM_Call(cgvm, CG_GET_TAG, clientNum, tagname, or); +} diff --git a/src/engine/client/cl_cin.c b/src/engine/client/cl_cin.c new file mode 100644 index 0000000000..87bb642f1a --- /dev/null +++ b/src/engine/client/cl_cin.c @@ -0,0 +1,1845 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: cl_cin.c + * + * desc: video and cinematic playback + * + * + * cl_glconfig.hwtype trtypes 3dfx/ragepro need 256x256 + * + *****************************************************************************/ + +//#define ADAPTED_TO_STREAMING_SOUND +// (SA) MISSIONPACK MERGE +// s_rawend for wolf is [] and for q3 is just a single value +// I need to ask Ryan if it's as simple as a constant index or +// if some more coding needs to be done. + + +#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 int s_soundtime; + +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 enum +{ + FT_ROQ = 0, // normal roq (vq3 stuff) + FT_OGM // ogm(ogg wrapper, vorbis audio, xvid/theora video) for WoP +} filetype_t; + +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; + filetype_t fileType; +} cin_cache; + +static cinematics_t cin; +static cin_cache cinTable[MAX_VIDEO_HANDLES]; +static int currentHandle = -1; +static int CL_handle = -1; + +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)); +} + +/* +Frame_yuv_to_rgb24 +is used by the Theora(ogm) code + + moved the convertion into one function, to reduce the number of function-calls +*/ +void Frame_yuv_to_rgb24(const unsigned char *y, const unsigned char *u, const unsigned char *v, + int width, int height, int y_stride, int uv_stride, + int yWShift, int uvWShift, int yHShift, int uvHShift, unsigned int *output) +{ + int i, j, uvI; + long r, g, b, YY; + + for(j = 0; j < height; ++j) + { + for(i = 0; i < width; ++i) + { + + YY = (long)(ROQ_YY_tab[(y[(i >> yWShift) + (j >> yHShift) * y_stride])]); + uvI = (i >> uvWShift) + (j >> uvHShift) * uv_stride; + + r = (YY + ROQ_VR_tab[v[uvI]]) >> 6; + g = (YY + ROQ_UG_tab[u[uvI]] + ROQ_VG_tab[v[uvI]]) >> 6; + b = (YY + ROQ_UB_tab[u[uvI]]) >> 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; + + *output = LittleLong((r) | (g << 8) | (b << 16) | (255 << 24)); + ++output; + } + } + +} + + +/****************************************************************************** +* +* 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);i= 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, 1.0f ); + } + break; + case ZA_SOUND_STEREO: + if (!cinTable[currentHandle].silent) { + if (cinTable[currentHandle].numQuads == -1) { + S_Update(); + s_rawend = 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, 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: +* +******************************************************************************/ + +//FIXME: this isn't realy a "roq-shutdown" (it's more a CIN-shutdown, beside the file-closing) +static void RoQShutdown( void ) { + const char *s; + + if (!cinTable[currentHandle].buf) { + //FIXME: there could be something that should be "shutdowned" even if we don't have a output frame (at least in the ogm code) + 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; + if(cinTable[currentHandle].fileType == FT_OGM) + { + Cin_OGM_Shutdown(); + cinTable[currentHandle].buf = NULL; + } + currentHandle = -1; +} + +/* +================== +CIN_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; +} + + +/* +================== +CIN_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; + } + + if(cinTable[currentHandle].fileType == FT_OGM) + { + + if(Cin_OGM_Run(cinTable[currentHandle].startTime == 0 ? 0 : CL_ScaledMilliseconds() - cinTable[currentHandle].startTime)) + cinTable[currentHandle].status = FMV_EOF; + else + { + int newW, newH; + qboolean resolutionChange = qfalse; + + cinTable[currentHandle].buf = Cin_OGM_GetOutput(&newW, &newH); + + if(newW != cinTable[currentHandle].CIN_WIDTH) + { + cinTable[currentHandle].CIN_WIDTH = newW; + resolutionChange = qtrue; + } + if(newH != cinTable[currentHandle].CIN_HEIGHT) + { + cinTable[currentHandle].CIN_HEIGHT = newH; + resolutionChange = qtrue; + } + + if(resolutionChange) + { + cinTable[currentHandle].drawX = cinTable[currentHandle].CIN_WIDTH; + cinTable[currentHandle].drawY = cinTable[currentHandle].CIN_HEIGHT; + } + + cinTable[currentHandle].status = FMV_PLAY; + cinTable[currentHandle].dirty = qtrue; + } + + if(!cinTable[currentHandle].startTime) + cinTable[currentHandle].startTime = CL_ScaledMilliseconds(); + + if(cinTable[currentHandle].status == FMV_EOF) + { + if(cinTable[currentHandle].holdAtEnd) + { + cinTable[currentHandle].status = FMV_IDLE; + } + else if(cinTable[currentHandle].looping) + { + Cin_OGM_Shutdown(); + Cin_OGM_Init(cinTable[currentHandle].fileName); + cinTable[currentHandle].buf = NULL; + cinTable[currentHandle].startTime = 0; + cinTable[currentHandle].status = FMV_PLAY; + } + else + { + RoQShutdown(); +// Cin_OGM_Shutdown(); + } + } + + return cinTable[currentHandle].status; + } + + //FIXME? CL_ScaledMilliseconds already uses com_timescale (so I can't see that the com_timescale in here makes any sense at all O_o) + // 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; +} + +extern char *findExtension(const char *fni); // from snd_codec.c (just a nice implementation of getting a point to the extention) + +/* +================== +CIN_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; + char *fileextPtr; + + 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); + + fileextPtr = findExtension(name); // using the function from soundfile/audiocodec-detection + if(!Q_stricmp(fileextPtr, ".ogm")) + { + if(Cin_OGM_Init(name)) + { + Com_Printf("starting ogm-playback failed(%s)\n", arg); + cinTable[currentHandle].fileName[0] = 0; + Cin_OGM_Shutdown(); + return -1; + } + + cinTable[currentHandle].fileType = FT_OGM; + + CIN_SetExtents(currentHandle, x, y, w, h); + CIN_SetLooping(currentHandle, (systemBits & CIN_loop) != 0); + + 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; + +/* we will set this info after the first xvid-frame + cinTable[currentHandle].CIN_HEIGHT = DEFAULT_CIN_HEIGHT; + cinTable[currentHandle].CIN_WIDTH = DEFAULT_CIN_WIDTH; +*/ + + 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; + } + + if(cinTable[currentHandle].alterGameState) + { + cls.state = CA_CINEMATIC; + } + + cinTable[currentHandle].status = FMV_PLAY; + + return currentHandle; + } + + 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 = 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; +} + +/* +================== +CIN_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)< CA_DISCONNECTED && cls.state <= CA_ACTIVE ) { + return; + } + + Com_DPrintf("CL_PlayCinematic_f\n"); + if (cls.state == CA_CINEMATIC) { + SCR_StopCinematic(); + } + + arg = Cmd_Argv( 1 ); + s = Cmd_Argv(2); + + holdatend = qfalse; + if ((s && s[0] == '1') || Q_stricmp(arg,"demoend.roq")==0 || Q_stricmp(arg,"end.roq")==0) { + bits |= CIN_hold; + } + if (s && s[0] == '2') { + bits |= CIN_loop; + } + + S_StopAllSounds (); + + CL_handle = CIN_PlayCinematic( arg, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, bits ); + if (CL_handle >= 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--; + } + } +} \ No newline at end of file diff --git a/src/engine/client/cl_console.c b/src/engine/client/cl_console.c new file mode 100644 index 0000000000..d3abfd9be7 --- /dev/null +++ b/src/engine/client/cl_console.c @@ -0,0 +1,1169 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +// console.c + +#include "client.h" + + +int g_console_field_width = 78; + +#define COLNSOLE_COLOR COLOR_WHITE //COLOR_BLACK + +console_t con; + +cvar_t *con_debug; +cvar_t *con_conspeed; +cvar_t *con_notifytime; +cvar_t *con_autoclear; + +// Color and alpha for console +cvar_t *scr_conUseShader; + +cvar_t *scr_conColorAlpha; +cvar_t *scr_conColorRed; +cvar_t *scr_conColorBlue; +cvar_t *scr_conColorGreen; + +// Color and alpha for bar under console +cvar_t *scr_conBarHeight; + +cvar_t *scr_conBarColorAlpha; +cvar_t *scr_conBarColorRed; +cvar_t *scr_conBarColorBlue; +cvar_t *scr_conBarColorGreen; + +cvar_t *scr_conUseOld; +cvar_t *scr_conBarSize; +cvar_t *scr_conHeight; + + + +// DHM - Nerve :: Must hold CTRL + SHIFT + ~ to get console +cvar_t *con_restricted; + +#define DEFAULT_CONSOLE_WIDTH 78 + +static console_t cons[2]; + +vec4_t console_highlightcolor = { 0.5, 0.5, 0.2, 0.45 }; + + +/* +================ +Con_ToggleConsole_f +================ +*/ +void Con_ToggleConsole_f(void) +{ + con.acLength = 0; + + if(con_restricted->integer && (!keys[K_CTRL].down || !keys[K_SHIFT].down)) + { + return; + } + + // ydnar: persistent console input is more useful + // Arnout: added cvar + if(con_autoclear->integer) + { + Field_Clear(&g_consoleField); + } + + g_consoleField.widthInChars = g_console_field_width; + + Con_ClearNotify(); + + // ydnar: multiple console size support + if(cls.keyCatchers & KEYCATCH_CONSOLE) + { + cls.keyCatchers &= ~KEYCATCH_CONSOLE; + con.desiredFrac = 0.0; + } + else + { + cls.keyCatchers |= KEYCATCH_CONSOLE; + + // short console + if(keys[K_CTRL].down) + { + con.desiredFrac = (5.0 * SMALLCHAR_HEIGHT) / cls.glconfig.vidHeight; + } + // full console + else if(keys[K_ALT].down) + { + con.desiredFrac = 1.0; + } + // normal half-screen console + else + { + con.desiredFrac = 0.5; + } + } +} + +/* +================ +Con_MessageMode_f +================ +*/ +void Con_MessageMode_f(void) +{ + chat_team = qfalse; + Field_Clear(&chatField); + chatField.widthInChars = 30; + + cls.keyCatchers ^= KEYCATCH_MESSAGE; +} + +/* +================ +Con_MessageMode2_f +================ +*/ +void Con_MessageMode2_f(void) +{ + chat_team = qtrue; + Field_Clear(&chatField); + chatField.widthInChars = 25; + cls.keyCatchers ^= KEYCATCH_MESSAGE; +} + +/* +================ +Con_MessageMode3_f +================ +*/ +void Con_MessageMode3_f(void) +{ + chat_team = qfalse; + chat_buddy = qtrue; + Field_Clear(&chatField); + chatField.widthInChars = 26; + cls.keyCatchers ^= KEYCATCH_MESSAGE; +} + + +const short* Con_GetText( int console ) +{ + if( console >= 0 && console < lengthof( cons ) && cons[console].text ) { + return cons[console].text; + } else { + return NULL; + } +} + + +/* +================ +Con_Clear_f +================ +*/ +void Con_Clear_f(void) +{ + int i; + + for(i = 0; i < CON_TEXTSIZE; i++) + { + con.text[i] = (ColorIndex(COLNSOLE_COLOR) << 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 < con.linewidth; x++) + if((line[x] & 0xff) != ' ') + { + break; + } + if(x != con.linewidth) + { + break; + } + } + + // write the remaining lines + buffer[con.linewidth] = 0; + for(; l <= con.current; l++) + { + line = con.text + (l % con.totallines) * con.linewidth; + for(i = 0; i < con.linewidth; i++) + buffer[i] = line[i] & 0xff; + for(x = con.linewidth - 1; x >= 0; x--) + { + if(buffer[x] == ' ') + { + buffer[x] = 0; + } + else + { + break; + } + } + strcat(buffer, "\n"); + FS_Write(buffer, strlen(buffer), f); + } + + FS_FCloseFile(f); +} + +/* +================ +Con_Search_f + +Scroll up to the first console line containing a string +================ +*/ +void Con_Search_f (void) +{ + int l, i, x; + short *line; + char buffer[MAXPRINTMSG]; + int direction; + int c = Cmd_Argc(); + + if (c < 2) { + Com_Printf ("usage: %s <...>\n", Cmd_Argv(0)); + return; + } + + if (!Q_stricmp(Cmd_Argv(0), "searchDown")) { + direction = 1; + } else { + direction = -1; + } + + // check the lines + buffer[con.linewidth] = 0; + for (l = con.display - 1 + direction; l <= con.current && con.current - l < con.totallines; l += direction) { + line = con.text + (l%con.totallines)*con.linewidth; + for (i = 0; i < con.linewidth; i++) + buffer[i] = line[i] & 0xff; + for (x = con.linewidth - 1 ; x >= 0 ; x--) { + if (buffer[x] == ' ') + buffer[x] = 0; + else + break; + } + // Don't search commands + for (i = 1; i < c; i++) { + if (Q_stristr(buffer, Cmd_Argv(i))) { + con.display = l + 1; + if (con.display > con.current) + con.display = con.current; + return; + } + } + } +} + + +/* +================ +Con_Grep_f + +Find all console lines containing a string +================ +*/ +void Con_Grep_f (void) +{ + int l, x, i; + short *line; + char buffer[1024]; + char buffer2[1024]; + char printbuf[CON_TEXTSIZE]; + char *search; + char lastcolor; + + if (Cmd_Argc() != 2) + { + Com_Printf ("usage: grep \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> 8 != lastcolor) + { + lastcolor = line[i] >> 8; + buffer[x++] = Q_COLOR_ESCAPE; + buffer[x++] = lastcolor + '0'; + } + buffer[x++] = line[i] & 0xff; + } + for (x=con.linewidth-1 ; x>=0 ; x--) + { + if (buffer[x] == ' ') + buffer[x] = 0; + else + break; + } + // Don't search commands + strcpy(buffer2, buffer); + Q_CleanStr(buffer2); + if (Q_stristr(buffer2, search)) + { + strcat( printbuf, buffer ); + strcat( printbuf, "\n" ); + } + } + if ( printbuf[0] ) + Com_Printf( "%s", printbuf ); +} + + + +/* +================ +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]; + + if (cls.glconfig.vidWidth) { + if (scr_conUseOld->integer) { + width = cls.glconfig.vidWidth / SCR_ConsoleFontCharWidth('W'); + } else { + width = (cls.glconfig.vidWidth - 30) / SCR_ConsoleFontCharWidth('W'); + } + g_consoleField.widthInChars = width - Q_PrintStrlen(cl_consolePrompt->string) - 1; + } else { + width = 0; + } + + 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; i < con.linewidth; i++) + con.text[(con.current % con.totallines) * con.linewidth + i] = (ColorIndex(COLNSOLE_COLOR) << 8) | ' '; +} + +/* +================ +CL_ConsolePrint + +Handles cursor positioning, line wrapping, etc +All console printing must go through this in order to be logged to disk +If no console is visible, the text will appear at the top of the game window +================ +*/ +#if defined( _WIN32 ) && defined( NDEBUG ) +#pragma optimize( "g", off ) // SMF - msvc totally screws this function up with optimize on +#endif + +void CL_ConsolePrint(char *txt) +{ + int y; + int c, l; + int color; + qboolean skipnotify = qfalse; // NERVE - SMF + int prev; // NERVE - SMF + + // NERVE - SMF - work around for text that shows up in console but not in notify + if(!Q_strncmp(txt, "[skipnotify]", 12)) + { + skipnotify = qtrue; + txt += 12; + } + + // for some demos we don't want to ever show anything on the console + if(cl_noprint && cl_noprint->integer) + { + 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; + } + + if( !skipnotify && !( cls.keyCatchers & KEYCATCH_CONSOLE ) && strncmp( txt, "EXCL: ", 6 ) ) { + + + // feed the text to cgame + Cmd_SaveCmdContext( ); + Cmd_TokenizeString( txt ); + CL_GameConsoleText( ); + Cmd_RestoreCmdContext( ); + + + } + + + color = ColorIndex(COLNSOLE_COLOR); + + while((c = *txt) != 0) + { + if(Q_IsColorString(txt)) + { + if(*(txt + 1) == COLOR_NULL) + { + color = ColorIndex(COLNSOLE_COLOR); + } + else + { + color = ColorIndex(*(txt + 1)); + } + txt += 2; + continue; + } + + // count word length + for(l = 0; l < con.linewidth; l++) + { + if(txt[l] <= ' ' && txt[l] >= 0) + { + 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; + // rain - sign extension caused the character to carry over + // into the color info for high ascii chars; casting c to unsigned + con.text[y * con.linewidth + con.x] = (color << 8) | (unsigned char)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; + } + } +} + +#if defined( _WIN32 ) && defined( NDEBUG ) +#pragma optimize( "g", on ) // SMF - re-enabled optimization +#endif + +/* +============================================================================== + +DRAWING + +============================================================================== +*/ + + +/* +================ +Con_DrawInput + +Draw the editline after a ] prompt +================ +*/ +void Con_DrawInput (void) { + int y; + char prompt[ MAX_STRING_CHARS ]; + vec4_t color; + qtime_t realtime; + + if ( cls.state != CA_DISCONNECTED && !(cls.keyCatchers & KEYCATCH_CONSOLE ) ) { + return; + } + + Com_RealTime( &realtime ); + + y = con.vislines - ( SCR_ConsoleFontCharHeight() * 2 ) + 2 ; + + Com_sprintf( prompt, sizeof( prompt ), "^0[^3%02d%c%02d^0]^7 %s", realtime.tm_hour, (realtime.tm_sec & 1) ? ':' : ' ', realtime.tm_min, cl_consolePrompt->string ); + + color[0] = 1.0f; + color[1] = 1.0f; + color[2] = 1.0f; + color[3] = (scr_conUseOld->integer ? 1.0f : con.displayFrac * 2.0f); + + SCR_DrawSmallStringExt( con.xadjust + cl_conXOffset->integer, y, prompt, color, qfalse, qfalse ); + + Q_CleanStr( prompt ); + Field_Draw( &g_consoleField, con.xadjust + cl_conXOffset->integer + SCR_ConsoleFontStringWidth(prompt, strlen(prompt)), y, qtrue, qtrue, color[3] ); +} + + +/* +================ +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 && cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME)) + { + continue; + } + + for(x = 0; x < con.linewidth; x++) + { + if((text[x] & 0xff) == ' ') + { + continue; + } + if(((text[x] >> 8) & COLOR_BITS) != currentColor) + { + currentColor = (text[x] >> 8) & COLOR_BITS; + 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(cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME)) + { + return; + } + + // draw the chat line + if(cls.keyCatchers & KEYCATCH_MESSAGE) + { + if(chat_team) + { + char buf[128]; + + CL_TranslateString("say_team:", buf); + SCR_DrawBigString(8, v, buf, 1.0f, qfalse ); + skip = strlen(buf) + 2; + } + else if(chat_buddy) + { + char buf[128]; + + CL_TranslateString("say_fireteam:", buf); + SCR_DrawBigString(8, v, buf, 1.0f, qfalse ); + skip = strlen(buf) + 2; + } + else if(chat_irc) + { + char buf[128]; + + CL_TranslateString("say_irc:", buf); + SCR_DrawBigString(8, v, buf, 1.0f, qfalse ); + skip = strlen(buf) + 2; + } + else + { + char buf[128]; + + CL_TranslateString("say:", buf); + SCR_DrawBigString(8, v, buf, 1.0f, qfalse ); + skip = strlen(buf) + 1; + } + + Field_BigDraw(&chatField, skip * BIGCHAR_WIDTH, 232, 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; + float totalwidth; + float currentWidthLocation = 0; + + if (scr_conUseOld->integer) { + lines = cls.glconfig.vidHeight * frac; + if (lines <= 0) + return; + + if (lines > cls.glconfig.vidHeight ) + lines = cls.glconfig.vidHeight; + } else { + lines = cls.glconfig.vidHeight * frac; + } + + // on wide screens, we will center the text + if (!scr_conUseOld->integer) { + con.xadjust = 15; + } + SCR_AdjustFrom640( &con.xadjust, NULL, NULL, NULL ); + + // draw the background + if (scr_conUseOld->integer) { + y = frac * SCREEN_HEIGHT; + if ( y < 1 ) { + y = 0; + } + else { + if( scr_conUseShader->integer ) + { + SCR_DrawPic( 0, 0, SCREEN_WIDTH, y, cls.consoleShader ); + } + else + { + // This will be overwrote, so ill just abuse it here, no need to define another array + color[0] = scr_conColorRed->value; + color[1] = scr_conColorGreen->value; + color[2] = scr_conColorBlue->value; + color[3] = scr_conColorAlpha->value; + + SCR_FillRect( 0, 0, SCREEN_WIDTH, y, color ); + } + } + + color[0] = scr_conBarColorRed->value; + color[1] = scr_conBarColorGreen->value; + color[2] = scr_conBarColorBlue->value; + color[3] = scr_conBarColorAlpha->value; + + SCR_FillRect( 0, y, SCREEN_WIDTH, scr_conBarSize->value, color ); + } else { + color[0] = scr_conColorRed->value; + color[1] = scr_conColorGreen->value; + color[2] = scr_conColorBlue->value; + color[3] = frac * 2 * scr_conColorAlpha->value; + SCR_FillRect(10, 10, 620, 460 * scr_conHeight->integer * 0.01, color); + + color[0] = scr_conBarColorRed->value; + color[1] = scr_conBarColorGreen->value; + color[2] = scr_conBarColorBlue->value; + color[3] = frac * 2 * scr_conBarColorAlpha->value; + SCR_FillRect(10, 10, 620, 1, color); //top + SCR_FillRect(10, 460 * scr_conHeight->integer * 0.01 + 10, 621, 1, color); //bottom + SCR_FillRect(10, 10, 1, 460 * scr_conHeight->integer * 0.01, color); //left + SCR_FillRect(630, 10, 1, 460 * scr_conHeight->integer * 0.01, color); //right + } + + + // draw the version number + + color[0] = 1.0f; + color[1] = 1.0f; + color[2] = 1.0f; + color[3] = (scr_conUseOld->integer ? 1.0f : frac * 2.0f); + re.SetColor( color ); + + i = strlen( Q3_VERSION ); + totalwidth = SCR_ConsoleFontStringWidth( Q3_VERSION, i ) + cl_conXOffset->integer-20; + if (!scr_conUseOld->integer) { + totalwidth += 30; + } + for (x=0 ; xinteger+10; + + currentWidthLocation = 0; + for (x=0 ; xinteger) + rows++; + + y = lines - (SCR_ConsoleFontCharHeight()*3); + + // draw from the bottom up + if (con.display != con.current) + { + // draw arrows to show the buffer is backscrolled + color[0] = 1.0f; + color[1] = 0.0f; + color[2] = 0.0f; + color[3] = (scr_conUseOld->integer ? 1.0f : frac * 2.0f); + re.SetColor( color ); + for (x=0 ; xinteger ? 0 : 4); x+=4) + SCR_DrawConsoleFontChar( con.xadjust + (x+1)*SCR_ConsoleFontCharWidth('^'), y, '^' ); + y -= SCR_ConsoleFontCharHeight(); + rows--; + } + + row = con.display; + + if ( con.x == 0 ) { + row--; + } + + currentColor = 7; + color[0] = g_color_table[currentColor][0]; + color[1] = g_color_table[currentColor][1]; + color[2] = g_color_table[currentColor][2]; + color[3] = (scr_conUseOld->integer ? 1.0f : frac * 2.0f); + re.SetColor( color ); + + for (i=0 ; iinteger; + + if (row < 0) + break; + if (con.current - row >= 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; + color[0] = g_color_table[currentColor][0]; + color[1] = g_color_table[currentColor][1]; + color[2] = g_color_table[currentColor][2]; + color[3] = (scr_conUseOld->integer ? 1.0f : frac * 2.0f); + re.SetColor( color ); + } + + SCR_DrawConsoleFontChar( con.xadjust + currentWidthLocation, y, text[x] & 0xff ); + currentWidthLocation += SCR_ConsoleFontCharWidth( text[x] & 0xff ); + } + } + + // draw the input prompt, user text, and cursor if desired + Con_DrawInput (); + + re.SetColor( NULL ); +} +extern cvar_t *con_drawnotify; + +/* +================== +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(!(cls.keyCatchers & (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->integer) + { + Con_DrawNotify(); + } + } +} + +//================================================================ + +/* +================== +Con_RunConsole + +Scroll it up or down +================== +*/ +void Con_RunConsole (void) { + // decide on the destination height of the console + if ( cls.keyCatchers & KEYCATCH_CONSOLE ) + if (scr_conUseOld->integer) { + con.finalFrac = MAX(0.10, 0.01 * scr_conHeight->integer); // configured console percentage + } else { + con.finalFrac = 0.5; + } + 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(); + cls.keyCatchers &= ~KEYCATCH_CONSOLE; + con.finalFrac = 0; // none visible + con.displayFrac = 0; +} diff --git a/src/engine/client/cl_input.c b/src/engine/client/cl_input.c new file mode 100644 index 0000000000..95cea5b04f --- /dev/null +++ b/src/engine/client/cl_input.c @@ -0,0 +1,1699 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code”). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +// 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. + +=============================================================================== +*/ + +static kbutton_t kb[NUM_BUTTONS]; + +// Arnout: doubleTap button mapping +static kbuttons_t dtmapping[] = { + -1, // DT_NONE + KB_MOVELEFT, // DT_MOVELEFT + KB_MOVERIGHT, // DT_MOVERIGHT + KB_FORWARD, // DT_FORWARD + KB_BACK, // DT_BACK + KB_WBUTTONS4, // DT_LEANLEFT + KB_WBUTTONS5, // DT_LEANRIGHT + KB_UP // DT_UP +}; + +void IN_MLookDown(void){ + kb[KB_MLOOK].active = qtrue; +} + +void IN_MLookUp(void) +{ + kb[KB_MLOOK].active = 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(&kb[KB_UP]); +} +void IN_UpUp(void) +{ + IN_KeyUp(&kb[KB_UP]); +} +void IN_DownDown(void) +{ + IN_KeyDown(&kb[KB_DOWN]); +} +void IN_DownUp(void) +{ + IN_KeyUp(&kb[KB_DOWN]); +} +void IN_LeftDown(void) +{ + IN_KeyDown(&kb[KB_LEFT]); +} +void IN_LeftUp(void) +{ + IN_KeyUp(&kb[KB_LEFT]); +} +void IN_RightDown(void) +{ + IN_KeyDown(&kb[KB_RIGHT]); +} +void IN_RightUp(void) +{ + IN_KeyUp(&kb[KB_RIGHT]); +} +void IN_ForwardDown(void) +{ + IN_KeyDown(&kb[KB_FORWARD]); +} +void IN_ForwardUp(void) +{ + IN_KeyUp(&kb[KB_FORWARD]); +} +void IN_BackDown(void) +{ + IN_KeyDown(&kb[KB_BACK]); +} +void IN_BackUp(void) +{ + IN_KeyUp(&kb[KB_BACK]); +} +void IN_LookupDown(void) +{ + IN_KeyDown(&kb[KB_LOOKUP]); +} +void IN_LookupUp(void) +{ + IN_KeyUp(&kb[KB_LOOKUP]); +} +void IN_LookdownDown(void) +{ + IN_KeyDown(&kb[KB_LOOKDOWN]); +} +void IN_LookdownUp(void) +{ + IN_KeyUp(&kb[KB_LOOKDOWN]); +} +void IN_MoveleftDown(void) +{ + IN_KeyDown(&kb[KB_MOVELEFT]); +} +void IN_MoveleftUp(void) +{ + IN_KeyUp(&kb[KB_MOVELEFT]); +} +void IN_MoverightDown(void) +{ + IN_KeyDown(&kb[KB_MOVERIGHT]); +} +void IN_MoverightUp(void) +{ + IN_KeyUp(&kb[KB_MOVERIGHT]); +} + +void IN_SpeedDown(void) +{ + IN_KeyDown(&kb[KB_SPEED]); +} +void IN_SpeedUp(void) +{ + IN_KeyUp(&kb[KB_SPEED]); +} +void IN_StrafeDown(void) +{ + IN_KeyDown(&kb[KB_STRAFE]); +} +void IN_StrafeUp(void) +{ + IN_KeyUp(&kb[KB_STRAFE]); +} + +#ifdef USE_VOIP +void IN_VoipRecordDown(void) { + //IN_KeyDown(&in_voiprecord); + // Dushan + IN_KeyDown(&kb[KB_VOIPRECORD]); + Cvar_Set("cl_voipSend", "1"); +} + +void IN_VoipRecordUp(void) { + // IN_KeyUp(&in_voiprecord); + // Dushan + IN_KeyUp(&kb[KB_VOIPRECORD]); + Cvar_Set("cl_voipSend", "0"); +} +#endif + +void IN_Button0Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS0]); +} +void IN_Button0Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS0]); +} +void IN_Button1Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS1]); +} +void IN_Button1Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS1]); +} +void IN_UseItemDown(void) +{ + IN_KeyDown(&kb[KB_BUTTONS2]); +} +void IN_UseItemUp(void) +{ + IN_KeyUp(&kb[KB_BUTTONS2]); +} +void IN_Button2Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS2]); +} +void IN_Button2Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS2]); +} +void IN_Button3Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS3]); +} +void IN_Button3Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS3]); +} +void IN_Button4Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS4]); +} +void IN_Button4Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS4]); +} + +void IN_Button5Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS5]); +} + +void IN_Button5Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS5]); +} + +void IN_Button6Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS6]); +} + +void IN_Button6Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS6]); +} + +// +void IN_Button7Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS7]); +} + +void IN_Button7Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS7]); +} + +// +void IN_Button8Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS8]); +} + +void IN_Button8Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS8]); +} + +// +void IN_Button9Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS9]); +} + +void IN_Button9Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS9]); +} + +// +void IN_Button10Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS10]); +} + +void IN_Button10Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS10]); +} + +// +void IN_Button11Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS11]); +} + +void IN_Button11Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS11]); +} + +// +void IN_Button12Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS12]); +} + +void IN_Button12Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS12]); +} + +// +void IN_Button13Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS13]); +} + +void IN_Button13Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS13]); +} + +// +void IN_Button14Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS14]); +} + +void IN_Button14Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS14]); +} + +// +void IN_Button15Down(void) +{ + IN_KeyDown(&kb[KB_BUTTONS15]); +} + +void IN_Button15Up(void) +{ + IN_KeyUp(&kb[KB_BUTTONS15]); +} + +// Rafael activate +void IN_ActivateDown(void) +{ + IN_KeyDown(&kb[KB_BUTTONS6]); +} +void IN_ActivateUp(void) +{ + IN_KeyUp(&kb[KB_BUTTONS6]); +} + +// done. + +void IN_SprintDown(void) +{ + IN_KeyDown(&kb[KB_BUTTONS5]); +} +void IN_SprintUp(void) +{ + IN_KeyUp(&kb[KB_BUTTONS5]); +} + + +// wbuttons (wolf buttons) +void IN_Wbutton0Down(void) +{ + IN_KeyDown(&kb[KB_WBUTTONS0]); +} //----(SA) secondary fire button +void IN_Wbutton0Up(void) +{ + IN_KeyUp(&kb[KB_WBUTTONS0]); +} +void IN_ZoomDown(void) +{ + IN_KeyDown(&kb[KB_WBUTTONS1]); +} //----(SA) zoom key +void IN_ZoomUp(void) +{ + IN_KeyUp(&kb[KB_WBUTTONS1]); +} +void IN_ReloadDown(void) +{ + IN_KeyDown(&kb[KB_WBUTTONS3]); +} //----(SA) manual weapon re-load +void IN_ReloadUp(void) +{ + IN_KeyUp(&kb[KB_WBUTTONS3]); +} +void IN_LeanLeftDown(void) +{ + IN_KeyDown(&kb[KB_WBUTTONS4]); +} //----(SA) lean left +void IN_LeanLeftUp(void) +{ + IN_KeyUp(&kb[KB_WBUTTONS4]); +} +void IN_LeanRightDown(void) +{ + IN_KeyDown(&kb[KB_WBUTTONS5]); +} //----(SA) lean right +void IN_LeanRightUp(void) +{ + IN_KeyUp(&kb[KB_WBUTTONS5]); +} + +// Rafael Kick +// Arnout: now wbutton prone +void IN_ProneDown(void) +{ + IN_KeyDown(&kb[KB_WBUTTONS7]); +} +void IN_ProneUp(void) +{ + IN_KeyUp(&kb[KB_WBUTTONS7]); +} + +void IN_ButtonDown(void) +{ + IN_KeyDown(&kb[KB_BUTTONS1]); +} +void IN_ButtonUp(void) +{ + IN_KeyUp(&kb[KB_BUTTONS1]); +} + + +/*void IN_CenterView (void) { + cl.viewangles[PITCH] = -SHORT2ANGLE(cl.snap.ps.delta_angles[PITCH]); +}*/ + + +void IN_Notebook(void) +{ + //if ( cls.state == CA_ACTIVE && !clc.demoplaying ) { + //VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NOTEBOOK); // startup notebook + //} +} + +void IN_Help(void) +{ + if(cls.state == CA_ACTIVE && !clc.demoplaying) + { + VM_Call(uivm, UI_SET_ACTIVE_MENU, UIMENU_HELP); // startup help system + } +} + + +//========================================================================== + +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; + +cvar_t *cl_recoilPitch; + +cvar_t *cl_bypassMouseInput; // NERVE - SMF + +cvar_t *cl_doubletapdelay; + +/* +================ +CL_AdjustAngles + +Moves the local angle positions +================ +*/ +void CL_AdjustAngles(void) +{ + float speed; + + if(kb[KB_SPEED].active) + { + speed = 0.001 * cls.frametime * cl_anglespeedkey->value; + } + else + { + speed = 0.001 * cls.frametime; + } + + if(!kb[KB_STRAFE].active) + { + cl.viewangles[YAW] -= speed * cl_yawspeed->value * CL_KeyState(&kb[KB_RIGHT]); + cl.viewangles[YAW] += speed * cl_yawspeed->value * CL_KeyState(&kb[KB_LEFT]); + } + + cl.viewangles[PITCH] -= speed * cl_pitchspeed->value * CL_KeyState(&kb[KB_LOOKUP]); + cl.viewangles[PITCH] += speed * cl_pitchspeed->value * CL_KeyState(&kb[KB_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(kb[KB_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(kb[KB_STRAFE].active) + { + side += movespeed * CL_KeyState(&kb[KB_RIGHT]); + side -= movespeed * CL_KeyState(&kb[KB_LEFT]); + } + + side += movespeed * CL_KeyState(&kb[KB_MOVERIGHT]); + side -= movespeed * CL_KeyState(&kb[KB_MOVELEFT]); + +//----(SA) added + if(cmd->buttons & BUTTON_ACTIVATE) + { + if(side > 0) + { + cmd->wbuttons |= WBUTTON_LEANRIGHT; + } + else if(side < 0) + { + cmd->wbuttons |= WBUTTON_LEANLEFT; + } + + side = 0; // disallow the strafe when holding 'activate' + } +//----(SA) end + + up += movespeed * CL_KeyState(&kb[KB_UP]); + up -= movespeed * CL_KeyState(&kb[KB_DOWN]); + + forward += movespeed * CL_KeyState(&kb[KB_FORWARD]); + forward -= movespeed * CL_KeyState(&kb[KB_BACK]); + + // fretn - moved this to bg_pmove.c + //if (!(cl.snap.ps.persistant[PERS_HWEAPON_USE])) + //{ + cmd->forwardmove = ClampChar(forward); + cmd->rightmove = ClampChar(side); + cmd->upmove = ClampChar(up); + //} + + // Arnout: double tap + cmd->doubleTap = DT_NONE; // reset + if(com_frameTime - cl.doubleTap.lastdoubleTap > cl_doubletapdelay->integer + 150 + cls.frametime) + { // double tap only once every 500 msecs (add + // frametime for low(-ish) fps situations) + int i; + qboolean key_down; + + for(i = 1; i < DT_NUM; i++) + { + key_down = kb[dtmapping[i]].active || kb[dtmapping[i]].wasPressed; + + if(key_down && !cl.doubleTap.pressedTime[i]) + { + cl.doubleTap.pressedTime[i] = com_frameTime; + } + else if(!key_down && !cl.doubleTap.releasedTime[i] + && (com_frameTime - cl.doubleTap.pressedTime[i]) < (cl_doubletapdelay->integer + cls.frametime)) + { + cl.doubleTap.releasedTime[i] = com_frameTime; + } + else if(key_down && (com_frameTime - cl.doubleTap.pressedTime[i]) < (cl_doubletapdelay->integer + cls.frametime) + && (com_frameTime - cl.doubleTap.releasedTime[i]) < (cl_doubletapdelay->integer + cls.frametime)) + { + cl.doubleTap.pressedTime[i] = cl.doubleTap.releasedTime[i] = 0; + cmd->doubleTap = i; + cl.doubleTap.lastdoubleTap = com_frameTime; + } + else if(!key_down && (cl.doubleTap.pressedTime[i] || cl.doubleTap.releasedTime[i])) + { + if(com_frameTime - cl.doubleTap.pressedTime[i] >= (cl_doubletapdelay->integer + cls.frametime)) + { + cl.doubleTap.pressedTime[i] = cl.doubleTap.releasedTime[i] = 0; + } + } + } + } +} + +/* +================= +CL_MouseEvent +================= +*/ +void CL_MouseEvent(int dx, int dy, int time) +{ + if(cls.keyCatchers & KEYCATCH_UI) + { + + // NERVE - SMF - if we just want to pass it along to game + if(cl_bypassMouseInput->integer == 1) + { + cl.mouseDx[cl.mouseIndex] += dx; + cl.mouseDy[cl.mouseIndex] += dy; + } + else + { + VM_Call(uivm, UI_MOUSE_EVENT, dx, dy); + } + + } + else if(cls.keyCatchers & KEYCATCH_CGAME) + { + if(cl_bypassMouseInput->integer == 1) + { + cl.mouseDx[cl.mouseIndex] += dx; + cl.mouseDy[cl.mouseIndex] += dy; + } + else + { + 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_AccelEvent + +iPhone Accelerometer event +================= +*/ +#if defined (IPHONE) +void CL_AccelEvent( int pitch, int roll, int yaw ) { + cl.accelAngles[PITCH] = pitch; + cl.accelAngles[ROLL] = roll; + cl.accelAngles[YAW] = yaw; +} +#endif + +/* +================= +CL_JoystickMove +================= +*/ +void CL_JoystickMove(usercmd_t * cmd) +{ + int movespeed; + float anglespeed; + + if(kb[KB_SPEED].active ^ cl_run->integer) + { + movespeed = 2; + } + else + { + movespeed = 1; + cmd->buttons |= BUTTON_WALKING; + } + + if(kb[KB_SPEED].active) + { + anglespeed = 0.001 * cls.frametime * cl_anglespeedkey->value; + } + else + { + anglespeed = 0.001 * cls.frametime; + } + +#ifdef __MACOS__ + cmd->rightmove = ClampChar(cmd->rightmove + cl.joystickAxis[AXIS_SIDE]); +#else + if(!kb[KB_STRAFE].active) + { + cl.viewangles[YAW] += anglespeed * j_yaw->value * cl.joystickAxis[j_yaw_axis->integer]; + cmd->rightmove = ClampChar( cmd->rightmove + (int) (j_side->value * cl.joystickAxis[j_side_axis->integer]) ); + } + else + { + cl.viewangles[YAW] += anglespeed * j_side->value * cl.joystickAxis[j_side_axis->integer]; + cmd->rightmove = ClampChar( cmd->rightmove + (int) (j_yaw->value * cl.joystickAxis[j_yaw_axis->integer]) ); + } +#endif + if(kb[KB_MLOOK].active) + { + cl.viewangles[PITCH] += anglespeed * j_forward->value * cl.joystickAxis[j_forward_axis->integer]; + cmd->forwardmove = ClampChar( cmd->forwardmove + (int) (j_pitch->value * cl.joystickAxis[j_pitch_axis->integer]) ); + } + else + { + cl.viewangles[PITCH] += anglespeed * j_pitch->value * cl.joystickAxis[j_pitch_axis->integer]; + cmd->forwardmove = ClampChar( cmd->forwardmove + (int) (j_forward->value * cl.joystickAxis[j_forward_axis->integer]) ); + } + + cmd->upmove = ClampChar(cmd->upmove + cl.joystickAxis[AXIS_UP]); +} + +/* +================= +CL_Xbox360ControllerMove +================= +*/ + +void CL_Xbox360ControllerMove(usercmd_t * cmd) { + int movespeed; + float anglespeed; + + if(kb[KB_SPEED].active ^ cl_run->integer ) { + movespeed = 2; + } + else { + movespeed = 1; + cmd->buttons |= BUTTON_WALKING; + } + + if(kb[KB_SPEED].active) { + anglespeed = 0.001 * cls.frametime * cl_anglespeedkey->value; + } + else { + anglespeed = 0.001 * cls.frametime; + } + + cl.viewangles[PITCH] += anglespeed * cl_pitchspeed->value * (cl.joystickAxis[AXIS_PITCH] / 127.0f); + cl.viewangles[YAW] += anglespeed * cl_yawspeed->value * (cl.joystickAxis[AXIS_YAW] / 127.0f); + + cmd->rightmove = ClampChar(cmd->rightmove + cl.joystickAxis[AXIS_SIDE]); + 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; + + + // allow mouse smoothing + if(m_filter->integer) + { + mx = (cl.mouseDx[0] + cl.mouseDx[1]) * 0.5f; + my = (cl.mouseDy[0] + cl.mouseDy[1]) * 0.5f; + } + else + { + mx = cl.mouseDx[cl.mouseIndex]; + my = cl.mouseDy[cl.mouseIndex]; + } + cl.mouseIndex ^= 1; + cl.mouseDx[cl.mouseIndex] = 0; + cl.mouseDy[cl.mouseIndex] = 0; + + if (mx == 0.0f && my == 0.0f) + return; + + if (cl_mouseAccel->value != 0.0f) + { + if(cl_mouseAccelStyle->integer == 0) + { + float accelSensitivity; + float rate; + + rate = sqrt(mx * mx + my * my) / (float) frame_msec; + + accelSensitivity = cl_sensitivity->value + rate * cl_mouseAccel->value; + mx *= accelSensitivity; + my *= accelSensitivity; + + if(cl_showMouseRate->integer) + Com_Printf("rate: %f, accelSensitivity: %f\n", rate, accelSensitivity); + } + else + { + float rate[2]; + float power[2]; + + // sensitivity remains pretty much unchanged at low speeds + // cl_mouseAccel is a power value to how the acceleration is shaped + // cl_mouseAccelOffset is the rate for which the acceleration will have doubled the non accelerated amplification + // NOTE: decouple the config cvars for independent acceleration setup along X and Y? + + rate[0] = fabs(mx) / (float) frame_msec; + rate[1] = fabs(my) / (float) frame_msec; + power[0] = powf(rate[0] / cl_mouseAccelOffset->value, cl_mouseAccel->value); + power[1] = powf(rate[1] / cl_mouseAccelOffset->value, cl_mouseAccel->value); + + mx = cl_sensitivity->value * (mx + ((mx < 0) ? -power[0] : power[0]) * cl_mouseAccelOffset->value); + my = cl_sensitivity->value * (my + ((my < 0) ? -power[1] : power[1]) * cl_mouseAccelOffset->value); + +/* NERVE - SMF - this has moved to CG_CalcFov to fix zoomed-in/out transition movement bug + if ( cl.snap.ps.stats[STAT_ZOOMED_VIEW] ) { + if(cl.snap.ps.weapon == WP_SNIPERRIFLE) { + accelSensitivity *= 0.1; + } + else if(cl.snap.ps.weapon == WP_SNOOPERSCOPE) { + accelSensitivity *= 0.2; + } + } +*/ + if(cl_showMouseRate->integer) + Com_Printf("ratex: %f, ratey: %f, powx: %f, powy: %f\n", rate[0], rate[1], power[0], power[1]); + } + } + +// Ridah, experimenting with a slow tracking gun + + // Rafael - mg42 + if(cl.snap.ps.persistant[PERS_HWEAPON_USE]) + { + mx *= 2.5; //(accelSensitivity * 0.1); + my *= 2; //(accelSensitivity * 0.075); + } + else + { + mx *= cl_sensitivity->value; + my *= cl_sensitivity->value; + } + + // ingame FOV + mx *= cl.cgameSensitivity; + my *= cl.cgameSensitivity; + + // add mouse X/Y movement to cmd + if(kb[KB_STRAFE].active) + cmd->rightmove = ClampChar(cmd->rightmove + m_side->value * mx); + else + cl.viewangles[YAW] -= m_yaw->value * mx; + + if((kb[KB_MLOOK].active || cl_freelook->integer) && !kb[KB_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(kb[KB_BUTTONS0 + i].active || kb[KB_BUTTONS0 + i].wasPressed) + { + cmd->buttons |= 1 << i; + } + kb[KB_BUTTONS0 + i].wasPressed = qfalse; + } + + for(i = 0; i < 16; i++) + { // Arnout: this was i < 7, but there are 8 wbuttons + if(kb[KB_WBUTTONS0 + i].active || kb[KB_WBUTTONS0 + i].wasPressed) + { + cmd->wbuttons |= 1 << i; + } + kb[KB_WBUTTONS0 + i].wasPressed = qfalse; + } + + if(cls.keyCatchers && !cl_bypassMouseInput->integer) + { + 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 && (!cls.keyCatchers || cl_bypassMouseInput->integer)) + { + cmd->buttons |= BUTTON_ANY; + } + + // Arnout: clear 'waspressed' from double tap buttons + for(i = 1; i < DT_NUM; i++) + { + kb[dtmapping[i]].wasPressed = qfalse; + } +} + + +/* +============== +CL_FinishMove +============== +*/ +void CL_FinishMove(usercmd_t * cmd) +{ + int i; + + // copy the state that the cgame is currently sending + cmd->weapon = cl.cgameUserCmdValue; + + cmd->flags = cl.cgameFlags; + + cmd->identClient = cl.cgameMpIdentClient; // NERVE - SMF + + // 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; + float recoilAdd; + + VectorCopy(cl.viewangles, oldAngles); + + // keyboard angle adjustment + CL_AdjustAngles(); + + 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 or controller + if(cl_xbox360ControllerAvailable->integer) { + CL_Xbox360ControllerMove(&cmd); + } + else { + 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; + } + + // RF, set the kickAngles so aiming is effected + recoilAdd = cl_recoilPitch->value; + if(Q_fabs(cl.viewangles[PITCH] + recoilAdd) < 40) + { + cl.viewangles[PITCH] += recoilAdd; + } + // the recoilPitch has been used, so clear it out + cl_recoilPitch->value = 0; + + // 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(*cls.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 && !*cls.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(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; + } + + 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 + // NOTE TTimo: if you verbose this, you will see that there are quite a few duplicates + // typically several unacknowledged cp or userinfo commands stacked up + 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) + { + if((clc.voipFlags & VOIP_SPATIAL) || Com_IsVoipTarget(clc.voipTargets, sizeof(clc.voipTargets), -1)) + { + MSG_WriteByte (&buf, clc_voip); + MSG_WriteByte (&buf, clc.voipOutgoingGeneration); + MSG_WriteLong (&buf, clc.voipOutgoingSequence); + MSG_WriteByte (&buf, clc.voipOutgoingDataFrames); + MSG_WriteData (&buf, clc.voipTargets, sizeof(clc.voipTargets)); + MSG_WriteByte(&buf, clc.voipFlags); + 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_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 + { + // We have data, but no targets. Silently discard all data + clc.voipOutgoingDataSize = 0; + clc.voipOutgoingDataFrames = 0; + } + } +#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) + { + if(cl_showSend->integer) + { + Com_Printf("WARNING: 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); // ---- id (primary firing) + 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("+useitem", IN_UseItemDown); + Cmd_AddCommand("-useitem", IN_UseItemUp); + + Cmd_AddCommand("+salute", IN_Button3Down); //----(SA) salute + Cmd_AddCommand("-salute", IN_Button3Up); + 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); + + // Rafael Activate + Cmd_AddCommand("+activate", IN_ActivateDown); + Cmd_AddCommand("-activate", IN_ActivateUp); + // done. + + // Rafael Kick + // Arnout: now prone + Cmd_AddCommand("+prone", IN_ProneDown); + Cmd_AddCommand("-prone", IN_ProneUp); + // done + + Cmd_AddCommand("+dodge", IN_ProneDown); + Cmd_AddCommand("-dodge", IN_ProneUp); + + Cmd_AddCommand("+sprint", IN_SprintDown); + Cmd_AddCommand("-sprint", IN_SprintUp); + + + // wolf buttons + Cmd_AddCommand("+attack2", IN_Wbutton0Down); //----(SA) secondary firing + Cmd_AddCommand("-attack2", IN_Wbutton0Up); + Cmd_AddCommand("+zoom", IN_ZoomDown); // + Cmd_AddCommand("-zoom", IN_ZoomUp); + Cmd_AddCommand("+reload", IN_ReloadDown); // + Cmd_AddCommand("-reload", IN_ReloadUp); + Cmd_AddCommand("+leanleft", IN_LeanLeftDown); + Cmd_AddCommand("-leanleft", IN_LeanLeftUp); + Cmd_AddCommand("+leanright", IN_LeanRightDown); + Cmd_AddCommand("-leanright", IN_LeanRightUp); + + + Cmd_AddCommand("+mlook", IN_MLookDown); + Cmd_AddCommand("-mlook", IN_MLookUp); + + + //Cmd_AddCommand ("notebook",IN_Notebook); + Cmd_AddCommand("help", IN_Help); + +#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); +} + + +/* +============ +CL_ClearKeys +============ +*/ +void CL_ClearKeys(void) +{ + memset(kb, 0, sizeof(kb)); +} diff --git a/src/engine/client/cl_irc.c b/src/engine/client/cl_irc.c new file mode 100644 index 0000000000..52cf0dc9bf --- /dev/null +++ b/src/engine/client/cl_irc.c @@ -0,0 +1,2271 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1997-2001 Id Software, Inc. +Copyright (C) 2010 COR Entertainment, LLC. +Copyright (C) 2011 Dusan Jocic + +OpenWolf 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. + +OpenWolf 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 this program. If not, see . + +=========================================================================== +*/ + +// cl_irc.c -- irc client + +#ifndef HAVE_CONFIG_H +// #include "config.h" +#endif + +#include "client.h" +#include "../qcommon/htable.h" + + +#ifdef WIN32 +# include +# include + typedef SOCKET irc_socket_t; +#else +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + typedef int irc_socket_t; +# if !defined HAVE_CLOSESOCKET +# define closesocket close +# endif +# if !defined INVALID_SOCKET +# define INVALID_SOCKET (-1) +# endif +#endif + + +/* IRC control cvars */ +cvar_t * cl_IRC_connect_at_startup; +cvar_t * cl_IRC_server; +cvar_t * cl_IRC_channel; +cvar_t * cl_IRC_port; +cvar_t * cl_IRC_override_nickname; +cvar_t * cl_IRC_nickname; +cvar_t * cl_IRC_kick_rejoin; +cvar_t * cl_IRC_reconnect_delay; + + +/* + * Timing controls + * + * In order to avoid actively waiting like crazy, there are many parts of the + * IRC client code that need to sleep or wait for a timeout. However, if the + * wait is too long, it makes the whole thing unreactive to e.g. the irc_say + * command; if the wait is too shot, it starts using CPU time. + * + * The constants below control the timeouts. + */ + +#define IRC_TIMEOUT_MS 250 +#define IRC_TIMEOUT_US ( IRC_TIMEOUT_MS * 1000 ) +#define IRC_TIMEOUTS_PER_SEC ( 1000 / IRC_TIMEOUT_MS) + + + +/* Ctype-like macros */ +#define IS_UPPER(c) ( (c) >= 'A' && (c) <= 'Z' ) +#define IS_LOWER(c) ( (c) >= 'a' && (c) <= 'z' ) +#define IS_DIGIT(c) ( (c) >= '0' && (c) <= '9' ) +#define IS_CNTRL(c) ( (c) >= 0 && (c) <= 31 ) +#define IS_ALPHA(c) ( IS_UPPER(c) || IS_LOWER(c) ) +#define IS_ALNUM(c) ( IS_ALPHA(c) || IS_DIGIT(c) ) + + + +/* IRC command status; used to determine if connection should be re-attempted or not */ +#define IRC_CMD_SUCCESS 0 // Success +#define IRC_CMD_FATAL 1 // Fatal error, don't bother retrying +#define IRC_CMD_RETRY 2 // Recoverable error, command should be attempted again + + +/* Constants that indicate the state of the IRC thread. */ +#define IRC_THREAD_DEAD 0 // Thread is dead or hasn't been started +#define IRC_THREAD_INITIALISING 1 // Thread is being initialised +#define IRC_THREAD_CONNECTING 2 // Thread is attempting to connect +#define IRC_THREAD_SETNICK 3 // Thread is trying to set the player's + // nick +#define IRC_THREAD_CONNECTED 4 // Thread established a connection to + // the server and will attempt to join + // the channel +#define IRC_THREAD_JOINED 5 // Channel joined, ready to send or + // receive messages +#define IRC_THREAD_QUITTING 6 // The thread is being killed + + +/* Function that sets the thread status when the thread dies. Since that is + * system-dependent, it can't be done in the thread's main code. + */ +static void IRC_SetThreadDead( ); + + +/* Status of the IRC thread */ +static int IRC_ThreadStatus = IRC_THREAD_DEAD; + +/* Quit requested? */ +static qboolean IRC_QuitRequested; + +/* Socket handler */ +static irc_socket_t IRC_Socket; // Socket + + +/* + * The protocol parser uses a finite state machine, here are the various + * states' definitions as well as a variable containing the current state + * and various other variables for message building. + */ +#define IRC_PARSER_RECOVERY (-1) // Error recovery +#define IRC_PARSER_START 0 // Start of a message +#define IRC_PARSER_PFX_NOS_START 1 // Prefix start +#define IRC_PARSER_PFX_NOS 2 // Prefix, server or nick name +#define IRC_PARSER_PFX_USER_START 3 // Prefix, start of user name +#define IRC_PARSER_PFX_USER 4 // Prefix, user name +#define IRC_PARSER_PFX_HOST_START 5 // Prefix, start of host name +#define IRC_PARSER_PFX_HOST 6 // Prefix, host name +#define IRC_PARSER_COMMAND_START 7 // Start of command after a prefix +#define IRC_PARSER_STR_COMMAND 8 // String command +#define IRC_PARSER_NUM_COMMAND_2 9 // Numeric command, second character +#define IRC_PARSER_NUM_COMMAND_3 10 // Numeric command, third character +#define IRC_PARSER_NUM_COMMAND_4 11 // Numeric command end +#define IRC_PARSER_PARAM_START 12 // Parameter start +#define IRC_PARSER_MID_PARAM 13 // "Middle" parameter +#define IRC_PARSER_TRAILING_PARAM 14 // Trailing parameter +#define IRC_PARSER_LF 15 // End of line + +static int IRC_ParserState; +static qboolean IRC_ParserInMessage; +static qboolean IRC_ParserError; + + +/* + * According to RFC 1459, maximal message size is 512 bytes, including trailing + * CRLF. + */ + +#define IRC_MESSAGE_SIZE 512 +#define IRC_SEND_BUF_SIZE IRC_MESSAGE_SIZE +#define IRC_RECV_BUF_SIZE (IRC_MESSAGE_SIZE * 2) + + +/* + * IRC messages consist in: + * 1) an optional prefix, which contains either a server name or a nickname, + * 2) a command, which may be either a word or 3 numbers, + * 3) any number of arguments. + * + * RFC 2812 says that there are at most 14 "middle" parameters and a trailing + * parameter. However, UnrealIRCd does not respect this, and sends messages + * that contain an extra parameter. While the message in question could be + * ignored, it's better to avoid entering the error recovery state. + * + * Since we won't be handling messages in parallel, we will create a + * static record and use that to store everything, as we can definitely + * spare 130k of memory (note: we could have something smaller but it'd + * probably be a pointless exercise). + */ + + +#define irc_string_t(len) struct { \ + unsigned int length; \ + char string[ len ]; \ +} + + +#define IRC_MAX_NICK_LEN 64 +#define IRC_MAX_ARG_LEN 509 +#define IRC_MAX_PARAMS 16 + +struct irc_message_t { + // Prefix + irc_string_t(IRC_MAX_NICK_LEN) pfx_nickOrServer; + irc_string_t(IRC_MAX_NICK_LEN) pfx_user; + irc_string_t(IRC_MAX_NICK_LEN) pfx_host; + + // Command + irc_string_t(32) cmd_string; + + // Arguments + irc_string_t(IRC_MAX_ARG_LEN) arg_values[IRC_MAX_PARAMS]; + unsigned int arg_count; +}; + +static struct irc_message_t IRC_ReceivedMessage; + + +// Macros to access the message's various fields +#define IRC_String(N) (IRC_ReceivedMessage.N.string) +#define IRC_Length(N) (IRC_ReceivedMessage.N.length) + + +/* + * IRC command handlers are called when some command is received; + * they are stored in hash tables. + */ + +typedef int (*irc_handler_func_t)( ); +typedef int (*ctcp_handler_func_t)( qboolean is_channel , const char * message ); + +struct irc_handler_t { + char cmd_string[33]; + void * handler; +}; + +static hashtable_t IRC_Handlers; +static hashtable_t IRC_CTCPHandlers; + + +/* + * Username, nickname, etc... + */ + +struct irc_user_t { + char nick[16]; + int nicklen; + int nickattempts; + char username[16]; + char email[100]; +}; + +static struct irc_user_t IRC_User; + +/* + * Events that can be displayed and flags that apply to them. + */ + +#define IRC_EVT_SAY 0x00000000 // Standard message +#define IRC_EVT_ACT 0x00000001 // /me message +#define IRC_EVT_JOIN 0x00000002 // Join +#define IRC_EVT_PART 0x00000003 // Part +#define IRC_EVT_QUIT 0x00000004 // Quit +#define IRC_EVT_KICK 0x00000005 // Kick +#define IRC_EVT_NICK_CHANGE 0x00000006 // Nick change +#define IRC_EVTF_SELF 0x00000100 // Event applies to current user + +#define IRC_EventType(evt) ( evt & 0xff ) +#define IRC_EventIsSelf(evt) ( ( evt & IRC_EVTF_SELF ) == IRC_EVTF_SELF ) +#define IRC_MakeEvent(type,isself) ( IRC_EVT_##type | ( (isself) ? IRC_EVTF_SELF : 0 ) ) + +/* + * Rate limiters for various events. + * + * The rate limiter works on a per-event basis, it doesn't know nor care + * about users. + * Its threshold and increase constants (which will be scaled depending on + * the timing controls) determine the amount of responses per second, while + * also allowing "bursts". + */ + +/* Rate limiter threshold - above that, no response */ +#define IRC_LIMIT_THRESHOLD 3 + +/* Rate limiter increase per check */ +#define IRC_LIMIT_INCREASE 1 + +#define IRC_RL_MESSAGE 0 +#define IRC_RL_PING 1 +#define IRC_RL_VERSION 2 + +static unsigned int IRC_RateLimiter[ 3 ]; + +/*--------------------------------------------------------------------------*/ +/* FUNCTIONS THAT MANAGE IRC COMMAND HANDLERS */ +/*--------------------------------------------------------------------------*/ + +/* +================== +IRC_InitHandlers + +Initialises the handler tables +================== +*/ +static ID_INLINE void IRC_InitHandlers( ) { + IRC_Handlers = HT_Create( 100 , HT_FLAG_INTABLE | HT_FLAG_CASE , + sizeof( struct irc_handler_t ) , + HT_OffsetOfField( struct irc_handler_t , cmd_string ) , + 32 ); + IRC_CTCPHandlers = HT_Create( 100 , HT_FLAG_INTABLE | HT_FLAG_CASE , + sizeof( struct irc_handler_t ) , + HT_OffsetOfField( struct irc_handler_t , cmd_string ) , + 32 ); +} + +/* +================== +IRC_FreeHandlers + +Frees the list of handlers (used when the IRC thread dies). +================== +*/ +static void IRC_FreeHandlers() { + HT_Destroy( IRC_Handlers ); + HT_Destroy( IRC_CTCPHandlers ); +} + +/* +================== +IRC_AddHandler + +Registers a new IRC command handler. +================== +*/ +static ID_INLINE void IRC_AddHandler( const char * command , irc_handler_func_t handler ) { + qboolean created; + struct irc_handler_t* rv; + rv = HT_GetItem( IRC_Handlers , command , &created ); + assert( created ); + rv->handler = handler; +} + +/* +================== +IRC_AddCTCPHandler + +Registers a new CTCP command handler. +================== +*/ +static void IRC_AddCTCPHandler( const char * command , ctcp_handler_func_t handler ) { + qboolean created; + struct irc_handler_t * rv; + rv = HT_GetItem( IRC_CTCPHandlers , command , &created ); + assert( created ); + rv->handler = handler; +} + +/* +================== +IRC_ExecuteHandler + +Executes the command handler for the currently stored command. If there is +no registered handler matching the command, ignore it. +================== +*/ +static int IRC_ExecuteHandler() { + struct irc_handler_t * handler; + handler = HT_GetItem( IRC_Handlers , IRC_String(cmd_string) , NULL ); + if ( handler == NULL ) + return IRC_CMD_SUCCESS; + return ((irc_handler_func_t)(handler->handler))( ); +} + +/* +================== +IRC_ExecuteCTCPHandler + +Executes a CTCP command handler. +================== +*/ +static int IRC_ExecuteCTCPHandler( const char * command , qboolean is_channel , const char *argument ) { + struct irc_handler_t * handler; + handler = HT_GetItem( IRC_CTCPHandlers , command , NULL ); + if ( handler == NULL ) + return IRC_CMD_SUCCESS; + return ((ctcp_handler_func_t)(handler->handler))( is_channel , argument ); +} + + + +/*--------------------------------------------------------------------------*/ +/* IRC DELAYED EXECUTION */ +/*--------------------------------------------------------------------------*/ + +/* Structure for the delayed execution queue */ +struct irc_delayed_t { + irc_handler_func_t handler; // Handler to call + int time_left; // "Time" left before call + struct irc_delayed_t * next; // Next record +}; + +/* Delayed execution queue head & tail */ +static struct irc_delayed_t * IRC_DEQueue = NULL; + +/* +================== +IRC_SetTimeout + +This function sets an IRC handler function to be executed after some time. +================== +*/ +static void IRC_SetTimeout( irc_handler_func_t function , int time ) { + struct irc_delayed_t * qe , * find; + assert( time > 0 ); + + // Create entry + qe = (struct irc_delayed_t *) malloc( sizeof( struct irc_delayed_t ) ); + qe->handler = function; + qe->time_left = time * IRC_TIMEOUTS_PER_SEC; + + // Find insert location + if ( IRC_DEQueue ) { + if ( IRC_DEQueue->time_left >= time ) { + qe->next = IRC_DEQueue; + IRC_DEQueue = qe; + } else { + find = IRC_DEQueue; + while ( find->next && find->next->time_left < time ) + find = find->next; + qe->next = find->next; + find->next = qe; + } + } else { + qe->next = NULL; + IRC_DEQueue = qe; + } +} + +/* +================== +IRC_DequeueDelayed + +This function dequeues an entry from the delayed execution queue. +================== +*/ +static qboolean IRC_DequeueDelayed() { + struct irc_delayed_t * found; + + if ( ! IRC_DEQueue ) + return qfalse; + + found = IRC_DEQueue; + IRC_DEQueue = found->next; + free( found ); + return qtrue; +} + +/* +================== +IRC_ProcessDEQueue + +This function deletes all remaining entries from the delayed execution queue +================== +*/ +static void IRC_FlushDEQueue() { + while ( IRC_DequeueDelayed( ) ) { + // PURPOSEDLY EMPTY + } +} + +/* +================== +IRC_ProcessDEQueue + +This function processes the delayed execution queue. +================== +*/ +static int IRC_ProcessDEQueue( ) { + struct irc_delayed_t * iter; + int err_code; + + iter = IRC_DEQueue; + while ( iter ) { + if ( iter->time_left == 1 ) { + err_code = (iter->handler)( ); + IRC_DequeueDelayed( ); + if ( err_code != IRC_CMD_SUCCESS ) + return err_code; + iter = IRC_DEQueue; + } else { + iter->time_left --; + iter = iter->next; + } + } + + return IRC_CMD_SUCCESS; +} + + + +/*--------------------------------------------------------------------------*/ +/* IRC MESSAGE PARSER */ +/*--------------------------------------------------------------------------*/ + + +/* Parser macros, 'cause I'm lazy */ +#define P_SET_STATE(S) IRC_ParserState = IRC_PARSER_##S +#define P_INIT_MESSAGE(S) { \ + P_SET_STATE(S); \ + IRC_ParserInMessage = qtrue; \ + memset( &IRC_ReceivedMessage , 0 , sizeof( struct irc_message_t ) ); \ +} +#if defined DEBUG_DUMP_IRC +#define P_ERROR(S) { \ + if ( ! IRC_ParserError ) { \ + Com_Printf( "IRC PARSER ERROR (state: %d , received: %d)\n" , IRC_ParserState , next ); \ + } \ + P_SET_STATE(S); \ + IRC_ParserError = qtrue; \ +} +#else // defined DEBUG_DUMP_IRC +#define P_ERROR(S) { \ + P_SET_STATE(S); \ + IRC_ParserError = qtrue; \ +} +#endif // defined DEBUG_DUMP_IRC +#define P_AUTO_ERROR { \ + if ( next == '\r' ) { \ + P_ERROR(LF); \ + } else { \ + P_ERROR(RECOVERY); \ + } \ +} +#define P_INIT_STRING(S) { \ + IRC_ReceivedMessage.S.string[0] = next; \ + IRC_ReceivedMessage.S.length = 1; \ +} +#define P_ADD_STRING(S) { \ + if ( IRC_ReceivedMessage.S.length == sizeof( IRC_ReceivedMessage.S.string ) - 1 ) { \ + P_ERROR(RECOVERY); \ + } else { \ + IRC_ReceivedMessage.S.string[IRC_ReceivedMessage.S.length ++] = next; \ + } \ +} +#define P_NEXT_PARAM { \ + if ( ( ++ IRC_ReceivedMessage.arg_count ) == IRC_MAX_PARAMS ) { \ + P_ERROR(RECOVERY); \ + } \ +} +#define P_START_PARAM { \ + if ( ( ++ IRC_ReceivedMessage.arg_count ) == IRC_MAX_PARAMS ) { \ + P_ERROR(RECOVERY); \ + } else \ + P_INIT_STRING(arg_values[IRC_ReceivedMessage.arg_count - 1]) \ +} +#define P_ADD_PARAM P_ADD_STRING(arg_values[IRC_ReceivedMessage.arg_count - 1]) + +/* +================== +IRC_DumpMessage + +Main parsing function that uses a FSM to parse one character at a time. +Returns true when a full message is read and no error has occured. +================== +*/ +static qboolean IRC_Parser( char next ) { + qboolean has_msg = qfalse; + + switch ( IRC_ParserState ) { + + /* Initial state; clear the message, then check input. ':' + * indicates there is a prefix, a digit indicates a numeric + * command, an upper-case letter indicates a string command. + * It's also possible we received an empty line - just skip + * it. Anything else is an error. + */ + case IRC_PARSER_START: + IRC_ParserError = qfalse; + IRC_ParserInMessage = qfalse; + if ( next == ':' ) { + P_INIT_MESSAGE(PFX_NOS_START); + } else if ( next == '\r' ) { + P_SET_STATE(LF); + } else if ( IS_DIGIT( next ) ) { + P_INIT_MESSAGE(NUM_COMMAND_2); + P_INIT_STRING(cmd_string); + } else if ( IS_UPPER( next ) ) { + P_INIT_MESSAGE(STR_COMMAND); + P_INIT_STRING(cmd_string); + } else { + P_ERROR(RECOVERY); + } + break; + + /* + * Start of prefix; anything is accepted, except for '!', '@', ' ' + * and control characters which all cause an error recovery. + */ + case IRC_PARSER_PFX_NOS_START: + if ( next == '!' || next == '@' || next == ' ' || IS_CNTRL( next ) ) { + P_AUTO_ERROR; + } else { + P_SET_STATE(PFX_NOS); + P_INIT_STRING(pfx_nickOrServer); + } + break; + + /* + * Prefix, server or nick name. Control characters cause an error, + * ' ', '!' and '@' cause state changes. + */ + case IRC_PARSER_PFX_NOS: + if ( next == '!' ) { + P_SET_STATE(PFX_USER_START); + } else if ( next == '@' ) { + P_SET_STATE(PFX_HOST_START); + } else if ( next == ' ' ) { + P_SET_STATE(COMMAND_START); + } else if IS_CNTRL( next ) { + P_AUTO_ERROR; + } else { + P_ADD_STRING(pfx_nickOrServer); + } + break; + + /* + * Start of user name; anything goes, except for '!', '@', ' ' + * and control characters which cause an error. + */ + case IRC_PARSER_PFX_USER_START: + if ( next == '!' || next == '@' || next == ' ' || IS_CNTRL( next ) ) { + P_AUTO_ERROR; + } else { + P_SET_STATE(PFX_USER); + P_INIT_STRING(pfx_user); + } + break; + + /* + * User name; '@' will cause state changes, '!' , ' ' and + * control characters will cause errors. + */ + case IRC_PARSER_PFX_USER: + if ( next == '@' ) { + P_SET_STATE(PFX_HOST_START); + } else if ( next == '!' || next == ' ' || IS_CNTRL( next ) ) { + P_AUTO_ERROR; + } else { + P_ADD_STRING(pfx_user); + } + break; + + /* + * Start of host name; anything goes, except for '!', '@', ' ' + * and control characters which cause an error. + */ + case IRC_PARSER_PFX_HOST_START: + if ( next == '!' || next == '@' || next == ' ' || IS_CNTRL( next ) ) { + P_AUTO_ERROR; + } else { + P_SET_STATE(PFX_HOST); + P_INIT_STRING(pfx_host); + } + break; + + /* + * Host name; ' ' will cause state changes, '!' and control + * characters will cause errors. + */ + case IRC_PARSER_PFX_HOST: + if ( next == ' ' ) { + P_SET_STATE(COMMAND_START); + } else if ( next == '!' || next == '@' || IS_CNTRL( next ) ) { + P_AUTO_ERROR; + } else { + P_ADD_STRING(pfx_host); + } + break; + + /* + * Start of command, will accept start of numeric and string + * commands; anything else is an error. + */ + case IRC_PARSER_COMMAND_START: + if ( IS_DIGIT( next ) ) { + P_SET_STATE(NUM_COMMAND_2); + P_INIT_STRING(cmd_string); + } else if ( IS_UPPER( next ) ) { + P_SET_STATE(STR_COMMAND); + P_INIT_STRING(cmd_string); + } else { + P_AUTO_ERROR; + } + break; + + /* + * String command. Uppercase letters will cause the parser + * to continue on string commands, ' ' indicates a parameter + * is expected, '\r' means we're done. Anything else is an + * error. + */ + case IRC_PARSER_STR_COMMAND: + if ( next == ' ' ) { + P_SET_STATE(PARAM_START); + } else if ( next == '\r' ) { + P_SET_STATE(LF); + } else if ( IS_UPPER( next ) ) { + P_ADD_STRING(cmd_string); + } else { + P_ERROR(RECOVERY); + } + break; + + /* + * Second/third digit of numeric command; anything but a digit + * is an error. + */ + case IRC_PARSER_NUM_COMMAND_2: + case IRC_PARSER_NUM_COMMAND_3: + if ( IS_DIGIT( next ) ) { + IRC_ParserState ++; + P_ADD_STRING(cmd_string); + } else { + P_AUTO_ERROR; + } + break; + + /* + * End of numeric command, could be a ' ' or a '\r'. + */ + case IRC_PARSER_NUM_COMMAND_4: + if ( next == ' ' ) { + P_SET_STATE(PARAM_START); + } else if ( next == '\r' ) { + P_SET_STATE(LF); + } else { + P_ERROR(RECOVERY); + } + break; + + /* + * Start of parameter. ':' means it's a trailing parameter, + * spaces and control characters shouldn't be here, and + * anything else is a "middle" parameter. + */ + case IRC_PARSER_PARAM_START: + if ( next == ':' ) { + P_SET_STATE(TRAILING_PARAM); + P_NEXT_PARAM; + } else if ( next == '\r' ) { + P_SET_STATE(LF); + } else if ( IS_CNTRL( next ) ) { + P_AUTO_ERROR; + } else if ( next != ' ' ) { + if ( next & 0x80 ) + next = '?'; + P_SET_STATE(MID_PARAM); + P_START_PARAM; + } + break; + + /* + * "Middle" parameter; ' ' means there's another parameter coming, + * '\r' means the end of the message, control characters are not + * accepted, anything else is part of the parameter. + */ + case IRC_PARSER_MID_PARAM: + if ( next == ' ' ) { + P_SET_STATE(PARAM_START); + } else if ( next == '\r' ) { + P_SET_STATE(LF); + } else if ( IS_CNTRL( next ) ) { + P_ERROR(RECOVERY); + } else { + if ( next & 0x80 ) + next = '?'; + P_ADD_PARAM; + } + break; + + /* + * Trailing parameter; '\r' means the end of the command, + * and anything else is just added to the string. + */ + case IRC_PARSER_TRAILING_PARAM: + if ( next == '\r' ) { + P_SET_STATE(LF); + } else { + if ( next & 0x80 ) { + next = '?'; + } + P_ADD_PARAM; + } + break; + + /* + * End of line, expect '\n'. If found, we may have a message + * to handle (unless there were errors). Anything else is an + * error. + */ + case IRC_PARSER_LF: + if ( next == '\n' ) { + has_msg = IRC_ParserInMessage; + P_SET_STATE(START); + } else { + P_AUTO_ERROR; + } + break; + + /* + * Error recovery: wait for an '\r'. + */ + case IRC_PARSER_RECOVERY: + if ( next == '\r' ) + P_SET_STATE(LF); + break; + } + + return has_msg && !IRC_ParserError; +} + +/* +================== +IRC_DumpMessage + +Debugging function that dumps the IRC message. +================== +*/ +#ifdef DEBUG_DUMP_IRC +static void IRC_DumpMessage() { + int i; + + Com_Printf( "----------- IRC MESSAGE RECEIVED -----------\n" ); + Com_Printf( " (pfx) nick/server .... [%.3d]%s\n" , IRC_Length( pfx_nickOrServer ) , IRC_String( pfx_nickOrServer ) ); + Com_Printf( " (pfx) user ........... [%.3d]%s\n" , IRC_Length( pfx_user ) , IRC_String( pfx_user ) ); + Com_Printf( " (pfx) host ........... [%.3d]%s\n" , IRC_Length( pfx_host ) , IRC_String( pfx_host ) ); + Com_Printf( " command string ....... [%.3d]%s\n" , IRC_Length( cmd_string ) , IRC_String( cmd_string ) ); + Com_Printf( " arguments ............ %.3d\n" , IRC_ReceivedMessage.arg_count ); + for ( i = 0 ; i < IRC_ReceivedMessage.arg_count ; i ++ ) { + Com_Printf( " ARG %d = [%.3d]%s\n" , i + 1 , IRC_Length( arg_values[ i ] ) , IRC_String( arg_values[ i ] ) ); + } +} +#endif // DEBUG_DUMP_IRC + +/*--------------------------------------------------------------------------*/ +/* "SYSTEM" FUNCTIONS */ +/*--------------------------------------------------------------------------*/ + +/* +================== +IRC_HandleError +================== +*/ +#ifdef WIN32 +static void IRC_HandleError(void) { + switch ( WSAGetLastError() ) { + case 0: // No error + return; + + case WSANOTINITIALISED : + Com_Printf("Unable to initialise socket.\n"); + break; + case WSAEAFNOSUPPORT : + Com_Printf("The specified address family is not supported.\n"); + break; + case WSAEADDRNOTAVAIL : + Com_Printf("Specified address is not available from the local machine.\n"); + break; + case WSAECONNREFUSED : + Com_Printf("The attempt to connect was forcefully rejected.\n"); + break; + case WSAEDESTADDRREQ : + Com_Printf("address destination address is required.\n"); + break; + case WSAEFAULT : + Com_Printf("The namelen argument is incorrect.\n"); + break; + case WSAEINVAL : + Com_Printf("The socket is not already bound to an address.\n"); + break; + case WSAEISCONN : + Com_Printf("The socket is already connected.\n"); + break; + case WSAEADDRINUSE : + Com_Printf("The specified address is already in use.\n"); + break; + case WSAEMFILE : + Com_Printf("No more file descriptors are available.\n"); + break; + case WSAENOBUFS : + Com_Printf("No buffer space available. The socket cannot be created.\n"); + break; + case WSAEPROTONOSUPPORT : + Com_Printf("The specified protocol is not supported.\n"); + break; + case WSAEPROTOTYPE : + Com_Printf("The specified protocol is the wrong type for this socket.\n"); + break; + case WSAENETUNREACH : + Com_Printf("The network can't be reached from this host at this time.\n"); + break; + case WSAENOTSOCK : + Com_Printf("The descriptor is not a socket.\n"); + break; + case WSAETIMEDOUT : + Com_Printf("Attempt timed out without establishing a connection.\n"); + break; + case WSAESOCKTNOSUPPORT : + Com_Printf("Socket type is not supported in this address family.\n"); + break; + case WSAENETDOWN : + Com_Printf("Network subsystem failure.\n"); + break; + case WSAHOST_NOT_FOUND : + Com_Printf("Authoritative Answer Host not found.\n"); + break; + case WSATRY_AGAIN : + Com_Printf("Non-Authoritative Host not found or SERVERFAIL.\n"); + break; + case WSANO_RECOVERY : + Com_Printf("Non recoverable errors, FORMERR, REFUSED, NOTIMP.\n"); + break; + case WSANO_DATA : + Com_Printf("Valid name, no data record of requested type.\n"); + break; + case WSAEINPROGRESS : + Com_Printf("address blocking Windows Sockets operation is in progress.\n"); + break; + default : + Com_Printf("Unknown connection error.\n"); + break; + } + + WSASetLastError( 0 ); +} +#elif defined __linux__ || defined MACOS_X || defined __FreeBSD__ +static void IRC_HandleError( void ) { + Com_Printf( "IRC socket connection error: %s\n" , strerror( errno ) ); +} +#endif + + +#if defined MSG_NOSIGNAL +# define IRC_SEND_FLAGS MSG_NOSIGNAL +#else +# define IRC_SEND_FLAGS 0 +#endif + +/* +================== +IRC_Send + +Attempt to format then send a message to the IRC server. Will return +true on success, and false if an overflow occurred or if send() failed. +================== +*/ +static int IRC_Send( const char * format , ... ) { + char buffer[ IRC_SEND_BUF_SIZE + 1 ]; + va_list args; + int len , sent; + + // Format message + va_start( args , format ); + len = vsnprintf( buffer , IRC_SEND_BUF_SIZE - 1 , format , args ); + va_end( args ); + if ( len >= IRC_SEND_BUF_SIZE - 1 ) { + // This is a bug, return w/ a fatal error + Com_Printf( "...IRC: send buffer overflow (%d characters)\n" , len ); + return IRC_CMD_FATAL; + } + + // Add CRLF terminator +#if defined DEBUG_DUMP_IRC + Com_Printf( "SENDING IRC MESSAGE: %s\n" , buffer ); +#endif + buffer[ len++ ] = '\r'; + buffer[ len++ ] = '\n'; + + // Send message + sent = send(IRC_Socket, buffer , len , IRC_SEND_FLAGS ); + if ( sent < len ) { + IRC_HandleError( ); + return IRC_CMD_RETRY; + } + + return IRC_CMD_SUCCESS; +} + + +/* + * This function is used to prevent the IRC thread from turning the CPU into + * a piece of molten silicium while it waits for the server to send data. + * + * If data is received, SUCCESS is returned; otherwise, RETRY will be returned + * on timeout and FATAL on error. + */ + +#ifdef WIN32 +# define SELECT_ARG 0 +# define SELECT_CHECK ( rv == -1 && WSAGetLastError() == WSAEINTR ) +#elif defined __linux__ || defined __FreeBSD__ || defined MACOS_X +# define SELECT_ARG ( IRC_Socket + 1 ) +# define SELECT_CHECK ( rv == -1 && errno == EINTR ) +#endif + +/* +================== +IRC_Wait +================== +*/ +static int IRC_Wait() { + struct timeval timeout; + fd_set read_set; + int rv; + + // Wait for data to be available + do { + FD_ZERO( &read_set ); + FD_SET( IRC_Socket, &read_set ); + timeout.tv_sec = 0; + timeout.tv_usec = IRC_TIMEOUT_US; + rv = select( SELECT_ARG , &read_set , NULL , NULL , &timeout ); + } while ( SELECT_CHECK ); + + // Something wrong happened + if ( rv < 0 ) { + IRC_HandleError( ); + return IRC_CMD_FATAL; + } + + return ( rv == 0 ) ? IRC_CMD_RETRY : IRC_CMD_SUCCESS; +} + +/* +================== +IRC_Sleep + +Wait for some seconds. +================== +*/ +static void IRC_Sleep( int seconds ) { + int i; + assert( seconds > 0 ); + for ( i = 0 ; i < seconds * IRC_TIMEOUTS_PER_SEC && !IRC_QuitRequested ; i ++ ) { +#ifdef WIN32 + Sleep( IRC_TIMEOUT_MS ); +#elif defined __linux__ + usleep( IRC_TIMEOUT_US ); +#endif + } +} + +/*--------------------------------------------------------------------------*/ +/* RATE LIMITS */ +/*--------------------------------------------------------------------------*/ + +/* +================== +IRC_CheckEventRate + +Checks if some action can be effected using the rate limiter. If it can, +the rate limiter's status will be updated. +================== +*/ +static ID_INLINE qboolean IRC_CheckEventRate( int event_type ) { + if ( IRC_RateLimiter[ event_type ] >= IRC_LIMIT_THRESHOLD * IRC_TIMEOUTS_PER_SEC ) + return qfalse; + IRC_RateLimiter[ event_type ] += IRC_LIMIT_INCREASE * IRC_TIMEOUTS_PER_SEC; + return qtrue; +} + +/* +================== +IRC_UpdateRateLimiter + +Decrease all non-zero rate limiter entries. +================== +*/ +static ID_INLINE void IRC_UpdateRateLimiter() { + int i; + for ( i = 0 ; i < sizeof( IRC_RateLimiter ) / sizeof( unsigned int ) ; i ++ ) + if ( IRC_RateLimiter[ i ] ) { + IRC_RateLimiter[ i ] --; + } +} + +/* +================== +IRC_InitRateLimiter + +Initialise the rate limiter. +================== +*/ +static ID_INLINE void IRC_InitRateLimiter() { + int i; + for ( i = 0 ; i < sizeof( IRC_RateLimiter ) / sizeof( unsigned int ) ; i ++ ) + IRC_RateLimiter[ i ] = 0; +} + +/*--------------------------------------------------------------------------*/ +/* DISPLAY CODE */ +/*--------------------------------------------------------------------------*/ + +/* +================== +IRC_NeutraliseString +================== +*/ +static void IRC_NeutraliseString( char * buffer , const char * source ) { + while ( *source ) { + char c = *source; + if ( IS_CNTRL( c ) ) { + *( buffer ++ ) = ' '; + } else if ( c & 0x80 ) { + *( buffer ++ ) = '?'; + } else if ( c == Q_COLOR_ESCAPE ) { + *( buffer ++ ) = Q_COLOR_ESCAPE; + *( buffer ++ ) = Q_COLOR_ESCAPE; + } else { + *( buffer ++ ) = c; + } + source ++; + } + *buffer = 0; +} + + +/* +================== +IRC_Display +================== +*/ +static void IRC_Display( int event , const char * nick , const char *message ) { + char buffer[ IRC_RECV_BUF_SIZE * 2 ]; + char nick_copy[ IRC_MAX_NICK_LEN * 2 ]; + char message_copy[ IRC_MAX_ARG_LEN * 2 ]; + const char *fmt_string; + qboolean has_nick; + qboolean has_message; + + // If we're quitting, just skip this + if ( IRC_QuitRequested ) + return; + + // Determine message format + switch ( IRC_EventType( event ) ) { + case IRC_EVT_SAY: + has_nick = has_message = qtrue; + if ( IRC_EventIsSelf( event ) ) { + fmt_string = "^2<^7%s^2> %s\n"; + } else if ( strstr( message , IRC_User.nick ) ) { + fmt_string = "^3<^7%s^3> %s\n"; + } else { + fmt_string = "^1<^7%s^1> %s\n"; + } + break; + case IRC_EVT_ACT: + has_nick = has_message = qtrue; + if ( IRC_EventIsSelf( event ) ) { + fmt_string = "^2* ^7%s^2 %s\n"; + } else if ( strstr( message , IRC_User.nick ) ) { + fmt_string = "^3* ^7%s^3 %s\n"; + } else { + fmt_string = "^1* ^7%s^1 %s\n"; + } + break; + case IRC_EVT_JOIN: + has_message = qfalse; + has_nick = !IRC_EventIsSelf( event ); + if ( has_nick ) { + fmt_string = "^5-> ^7%s^5 has entered the channel.\n"; + } else { + fmt_string = "^2Joined IRC chat.\n"; + } + break; + case IRC_EVT_PART: + // The AlienArena IRC client never parts, so it's + // someone else. + has_nick = qtrue; + has_message = ( message[0] != 0 ); + if ( has_message ) { + fmt_string = "^5<- ^7%s^5 has left the channel: %s.\n"; + } else { + fmt_string = "^5<- ^7%s^5 has left the channel.\n"; + } + break; + case IRC_EVT_QUIT: + has_nick = !IRC_EventIsSelf( event ); + if ( has_nick ) { + has_message = ( message[0] != 0 ); + if ( has_message ) { + fmt_string = "^5<- ^7%s^5 has quit: %s.\n"; + } else { + fmt_string = "^5<- ^7%s^5 has quit.\n"; + } + } else { + has_message = qtrue; + fmt_string = "^2Quit IRC chat: %s.\n"; + } + break; + case IRC_EVT_KICK: + has_nick = has_message = qtrue; + if ( IRC_EventIsSelf( event ) ) { + fmt_string = "^2Kicked by ^7%s^2: %s.\n"; + } else { + fmt_string = "^5<- ^7%s^5 has been kicked: %s.\n"; + } + break; + case IRC_EVT_NICK_CHANGE: + has_nick = has_message = qtrue; + if ( IRC_EventIsSelf( event ) ) { + fmt_string = "^2** ^7%s^2 is now known as ^7%s^2.\n"; + } else { + fmt_string = "^5** ^7%s^5 is now known as ^7%s^5.\n"; + } + break; + default: + has_nick = has_message = qfalse; + fmt_string = "unknown message received\n"; + break; + } + + // Neutralise required strings + if ( has_nick ) + IRC_NeutraliseString( nick_copy , nick ); + if ( has_message ) + IRC_NeutraliseString( message_copy , message ); + + // Format message + if ( has_nick && has_message ) { + sprintf( buffer , fmt_string , nick_copy , message_copy ); + } else if ( has_nick ) { + sprintf( buffer , fmt_string , nick_copy ); + } else if ( has_message ) { + sprintf( buffer , fmt_string , message_copy ); + } else { + strncpy( buffer , fmt_string , IRC_RECV_BUF_SIZE * 2 - 1 ); + } + buffer[ IRC_RECV_BUF_SIZE * 2 - 1 ] = 0; + + Com_Printf( "^1IRC: %s", buffer ); +} + +/*--------------------------------------------------------------------------*/ +/* IRC MESSAGE HANDLERS */ +/*--------------------------------------------------------------------------*/ + +/* +================== +IRC_SendNickname + +Send the user's nickname. +================== +*/ +static int IRC_SendNickname() { + return IRC_Send( "NICK %s\n" , IRC_User.nick ); +} + +/* +================== +IRC_JoinChannel + +Join the channel +================== +*/ +static int IRC_JoinChannel() { + return IRC_Send( "JOIN #%s\n" , cl_IRC_channel->string ); +} + +/* +================== +IRCH_Ping + +Handles a PING by replying with a PONG. +================== +*/ +static int IRCH_Ping() { + if ( IRC_ReceivedMessage.arg_count == 1 ) + return IRC_Send( "PONG :%s\n" , IRC_String( arg_values[ 0 ] ) ); + return IRC_CMD_SUCCESS; +} + +/* +================== +IRCH_ServerError + +Handles server errors +================== +*/ +static int IRCH_ServerError() { + if ( IRC_ThreadStatus == IRC_THREAD_QUITTING ) { + return IRC_CMD_SUCCESS; + } + + if ( IRC_ReceivedMessage.arg_count == 1 ) { + Com_Printf( "IRC: server error - %s\n" , IRC_String( arg_values[ 0 ] ) ); + } else { + Com_Printf( "IRC: server error\n" ); + } + return IRC_CMD_RETRY; +} + +/* +================== +IRCH_FatalError + +Some fatal error was received, the IRC thread must die. +================== +*/ +static int IRCH_FatalError() { + IRC_Display( IRC_MakeEvent(QUIT,1) , "" , "fatal error" ); + IRC_Send( "QUIT :Something went wrong\n" ); + return IRC_CMD_RETRY; +} + +/* +================== +IRCH_NickError + +Nickname error. If received while the thread is in the SETNICK state, +we might want to try again. Otherwise, we ignore the error as it should +not have been received anyway. +================== +*/ +#define RANDOM_NUMBER_CHAR ( '0' + rand() % 10 ) +static int IRCH_NickError() { + int i; + + if ( IRC_ThreadStatus == IRC_THREAD_SETNICK ) { + if ( ++ IRC_User.nickattempts == 4 ) { + IRC_Send( "QUIT :Could not set nickname\n" ); + return IRC_CMD_FATAL; + } + + if ( IRC_User.nicklen < 15 ) { + IRC_User.nick[ IRC_User.nicklen ++ ] = RANDOM_NUMBER_CHAR; + } else { + for ( i = IRC_User.nicklen - 3 ; i < IRC_User.nicklen ; i ++ ) { + IRC_User.nick[ i ] = RANDOM_NUMBER_CHAR; + } + } + + IRC_SetTimeout( IRC_SendNickname , 2 ); + } else { + Com_Printf( "...IRC: got spurious nickname error\n" ); + } + + return IRC_CMD_SUCCESS; +} + +/* +================== +IRCH_Connected + +Connection established, we will be able to join a channel +================== +*/ +static int IRCH_Connected() { + if ( IRC_ThreadStatus != IRC_THREAD_SETNICK ) { + IRC_Display( IRC_MakeEvent(QUIT,1) , "" , "IRC client bug\n" ); + IRC_Send( "QUIT :OpenWolf IRC bug!\n" ); + return IRC_CMD_RETRY; + } + IRC_ThreadStatus = IRC_THREAD_CONNECTED; + IRC_SetTimeout( &IRC_JoinChannel , 1 ); + return IRC_CMD_SUCCESS; +} + +/* +================== +IRCH_Joined + +Received JOIN +================== +*/ +static int IRCH_Joined() { + int event; + + if ( IRC_ThreadStatus < IRC_THREAD_CONNECTED ) { + IRC_Display( IRC_MakeEvent(QUIT,1) , "" , "IRC client bug\n" ); + IRC_Send( "QUIT :OpenWolf IRC bug!\n" ); + return IRC_CMD_RETRY; + } + + if ( !strcmp( IRC_String( pfx_nickOrServer ) , IRC_User.nick ) ) { + IRC_ThreadStatus = IRC_THREAD_JOINED; + event = IRC_MakeEvent(JOIN,1); + } else { + event = IRC_MakeEvent(JOIN,0); + } + IRC_Display( event , IRC_String( pfx_nickOrServer ) , NULL ); + return IRC_CMD_SUCCESS; +} + +/* +================== +IRCH_Part + +Received PART +================== +*/ +static int IRCH_Part() { + IRC_Display( IRC_MakeEvent(PART, 0) , IRC_String( pfx_nickOrServer ) , IRC_String( arg_values[ 1 ] ) ); + return IRC_CMD_SUCCESS; +} + +/* +================== +IRCH_Quit + +Received QUIT +================== +*/ +static int IRCH_Quit() { + IRC_Display( IRC_MakeEvent(QUIT, 0) , IRC_String( pfx_nickOrServer ) , IRC_String( arg_values[ 0 ] ) ); + return IRC_CMD_SUCCESS; +} + +/* +================== +IRCH_Kick + +Received KICK +================== +*/ +static int IRCH_Kick() { + if ( !strcmp( IRC_String( arg_values[ 1 ] ) , IRC_User.nick ) ) { + IRC_Display( IRC_MakeEvent(KICK, 1) , IRC_String( pfx_nickOrServer ) , IRC_String( arg_values[ 2 ] ) ); + if ( cl_IRC_kick_rejoin->integer > 0 ) { + IRC_ThreadStatus = IRC_THREAD_CONNECTED; + IRC_SetTimeout( &IRC_JoinChannel , cl_IRC_kick_rejoin->integer ); + } else { + IRC_Display( IRC_MakeEvent(QUIT, 1) , "" , "kicked from channel..\n" ); + IRC_Send( "QUIT :b&!\n" ); + return IRC_CMD_FATAL; + } + } else { + IRC_Display( IRC_MakeEvent(KICK, 0) , IRC_String( arg_values[ 1 ] ) , IRC_String( arg_values[ 2 ] ) ); + } + return IRC_CMD_SUCCESS; +} + +/* +================== +IRCH_Nick + +Received NICK + +While the AA client does not support changing the current nickname, +it is still possible to receive a NICK applying to the connected user +because of e.g. OperServ's SVSNICK command. +================== +*/ +static int IRCH_Nick() { + int event; + + if ( IRC_ReceivedMessage.arg_count != 1 ) + return IRC_CMD_SUCCESS; + + if ( !strcmp( IRC_String( pfx_nickOrServer ) , IRC_User.nick ) ) { + strncpy( IRC_User.nick , IRC_String( arg_values[ 0 ] ) , 15 ); + Com_Printf( "%s\n", IRC_User.nick ); + event = IRC_MakeEvent(NICK_CHANGE, 1); + } else { + event = IRC_MakeEvent(NICK_CHANGE, 0); + } + IRC_Display( event , IRC_String( pfx_nickOrServer ) , IRC_String( arg_values[ 0 ] ) ); + return IRC_CMD_SUCCESS; +} + +/* +================== +IRC_HandleMessage + +Handles an actual message. +================== +*/ +static int IRC_HandleMessage( qboolean is_channel , const char * string ) { + if ( is_channel ) { + IRC_Display( IRC_MakeEvent(SAY, 0) , IRC_String( pfx_nickOrServer ) , string ); + return IRC_CMD_SUCCESS; + } + + if ( IRC_CheckEventRate( IRC_RL_MESSAGE ) ) + return IRC_Send( "PRIVMSG %s :Sorry, OpenWolf's IRC client does not support private messages\n" , IRC_String( pfx_nickOrServer ) ); + return IRC_CMD_SUCCESS; +} + +/* +================== +IRC_HandleCTCP + +Splits a CTCP message into action and argument, then call +its handler (if there is one). +================== +*/ +static int IRC_HandleCTCP( qboolean is_channel , char * string , int string_len ) { + char * end_of_action; + + end_of_action = strchr( string , ' ' ); + if ( end_of_action == NULL ) { + end_of_action = string + string_len - 1; + *end_of_action = 0; + } else { + *( string + string_len - 1 ) = 0; + *end_of_action = 0; + end_of_action ++; + } + +#if defined DEBUG_DUMP_IRC + Com_Printf( "--- IRC/CTCP ---\n" ); + Com_Printf( " Command: %s\n Argument(s): %s\n" , string , end_of_action ); +#endif + + return IRC_ExecuteCTCPHandler( string , is_channel , end_of_action ); +} + +/* +================== +IRCH_PrivMsg + +Received PRIVMSG. + +This is either an actual message (to the channel or to the user) or a +CTCP command (action, version, etc...) +================== +*/ +static int IRCH_PrivMsg( ) +{ + qboolean is_channel; + + if ( IRC_ReceivedMessage.arg_count != 2 ) { + return IRC_CMD_SUCCESS; + } + + // Check message to channel (bail out if it isn't our channel) + is_channel = IRC_String( arg_values[ 0 ] )[ 0 ] == '#'; + if ( is_channel && strcmp( &( IRC_String( arg_values[ 0 ] )[ 1 ] ) , cl_IRC_channel->string ) ) + return IRC_CMD_SUCCESS; + + if ( IRC_Length( arg_values[ 1 ] ) > 2 + && IRC_String( arg_values[ 1 ] )[ 0 ] == 1 + && IRC_String( arg_values[ 1 ] )[ IRC_Length( arg_values[ 1 ] ) - 1 ] == 1 ) { + return IRC_HandleCTCP( is_channel , IRC_String( arg_values[ 1 ] ) + 1 , IRC_Length( arg_values[ 1 ] ) - 1 ); + } + + return IRC_HandleMessage( is_channel , IRC_String( arg_values[ 1 ] ) ); +} + + +/* +================== +IRCH_Banned + +User is banned. Leave and do not come back. +================== +*/ +static int IRCH_Banned() { + IRC_Display( IRC_MakeEvent(QUIT, 1) , "" , "banned from channel..\n" ); + IRC_Send( "QUIT :b&!\n" ); + return IRC_CMD_FATAL; +} + + + +/*--------------------------------------------------------------------------*/ +/* CTCP COMMAND HANDLERS */ +/*--------------------------------------------------------------------------*/ + +/* +================== +CTCP_Action + +Action command aka "/me" +================== +*/ +static int CTCP_Action( qboolean is_channel , const char * argument ) { + if ( !*argument ) + return IRC_CMD_SUCCESS; + + if ( is_channel ) { + IRC_Display( IRC_MakeEvent(ACT, 0) , IRC_String( pfx_nickOrServer ) , argument ); + return IRC_CMD_SUCCESS; + } + + if ( IRC_CheckEventRate( IRC_RL_MESSAGE ) ) + return IRC_Send( "PRIVMSG %s :Sorry, OpenWolf's IRC client does not support private messages\n" , IRC_String( pfx_nickOrServer ) ); + return IRC_CMD_SUCCESS; +} + +/* +================== +CTCP_Ping + +PING requests +================== +*/ +static int CTCP_Ping( qboolean is_channel , const char * argument ) { + if ( is_channel || !IRC_CheckEventRate( IRC_RL_PING ) ) + return IRC_CMD_SUCCESS; + + if ( *argument ) + return IRC_Send( "NOTICE %s :\001PING %s\001\n" , IRC_String( pfx_nickOrServer ) , argument ); + + return IRC_Send( "NOTICE %s :\001PING\001\n" , IRC_String( pfx_nickOrServer ) ); +} + +/* +================== +CTCP_Version + +VERSION requests, let's advertise AA a lil'. +================== +*/ +static int CTCP_Version( qboolean is_channel , const char * argument ) { + if ( is_channel || !IRC_CheckEventRate( IRC_RL_VERSION ) ) + return IRC_CMD_SUCCESS; + + return IRC_Send( "NOTICE %s :\001VERSION OpenWolf IRC client - v\n" Q3_VERSION "\001" , IRC_String( pfx_nickOrServer ) ); +} + +/*--------------------------------------------------------------------------*/ +/* MESSAGE SENDING */ +/*--------------------------------------------------------------------------*/ + +/* Maximal message length */ +#define IRC_MAX_SEND_LEN 400 + +/* + * The message sending queue is used to avoid having to send stuff from the + * game's main thread, as it could block or cause mix-ups in the printing + * function. + */ + +struct irc_sendqueue_t { + qboolean has_content; + qboolean is_action; + char string[IRC_MAX_SEND_LEN]; +}; + +/* Length of the IRC send queue */ +#define IRC_SENDQUEUE_SIZE 16 + +/* Index of the next message to process */ +static int IRC_SendQueue_Process = 0; +/* Index of the next message to write */ +static int IRC_SendQueue_Write = 0; + +/* The queue */ +static struct irc_sendqueue_t IRC_SendQueue[ IRC_SENDQUEUE_SIZE ]; + +/* +================== +IRC_InitSendQueue + +Initialise the send queue. +================== +*/ +static ID_INLINE void IRC_InitSendQueue() { + memset( &IRC_SendQueue , 0 , sizeof( IRC_SendQueue ) ); +} + +/* +================== +IRC_AddSendItem + +Writes an entry to the send queue. +================== +*/ +static qboolean IRC_AddSendItem( qboolean is_action , const char * string ) { + if ( IRC_SendQueue[ IRC_SendQueue_Write ].has_content ) + return qfalse; + + strcpy( IRC_SendQueue[ IRC_SendQueue_Write ].string , string ); + IRC_SendQueue[ IRC_SendQueue_Write ].is_action = is_action; + IRC_SendQueue[ IRC_SendQueue_Write ].has_content = qtrue; + IRC_SendQueue_Write = ( IRC_SendQueue_Write + 1 ) % IRC_SENDQUEUE_SIZE; + return qtrue; +} + +/* +================== +CL_OW_IRCSay + +Sends an IRC message (console command). +================== +*/ +void CL_OW_IRCSay() { + char m_sendstring[480]; + qboolean send_result; + + if (Cmd_Argc() < 2) { + Com_Printf ("usage: irc_say \n"); + return; + } + + if ( IRC_ThreadStatus != IRC_THREAD_JOINED ) { + Com_Printf("IRC: Not connected\n"); + return; + } + + memset( m_sendstring , 0 , sizeof( m_sendstring ) ); + strncpy( m_sendstring , Cmd_Args() , 479 ); + if ( m_sendstring[ 0 ] == 0 ) + return; + + if ( ( m_sendstring[ 0 ] == '/' || m_sendstring[ 0 ] == '.' ) && !Q_strnicmp( m_sendstring + 1 , "me " , 3 ) && m_sendstring[ 4 ] != 0 ) { + send_result = IRC_AddSendItem( qtrue , m_sendstring + 4 ); + } else { + send_result = IRC_AddSendItem( qfalse , m_sendstring ); + } + + if ( !send_result ) + Com_Printf( "IRC: flood detected, message not sent\n" ); +} + +/* +================== +IRC_ProcessSendQueue + +Processes the next item on the send queue, if any. +================== +*/ +static qboolean IRC_ProcessSendQueue() { + const char * fmt_string; + int event , rv; + + if ( !IRC_SendQueue[ IRC_SendQueue_Process ].has_content ) + return qtrue; + + if ( IRC_SendQueue[ IRC_SendQueue_Process ].is_action ) { + fmt_string = "PRIVMSG #%s :\001ACTION %s\001\n"; + event = IRC_MakeEvent(ACT, 1); + } else { + fmt_string = "PRIVMSG #%s :%s\n"; + event = IRC_MakeEvent(SAY, 1); + } + + rv = IRC_Send( fmt_string , cl_IRC_channel->string , IRC_SendQueue[ IRC_SendQueue_Process ].string ); + if ( rv == IRC_CMD_SUCCESS ) { + IRC_Display( event , IRC_User.nick , IRC_SendQueue[ IRC_SendQueue_Process ].string ); + } + IRC_SendQueue[ IRC_SendQueue_Process ].has_content = qfalse; + IRC_SendQueue_Process = ( IRC_SendQueue_Process + 1 ) % IRC_SENDQUEUE_SIZE; + return ( rv == IRC_CMD_SUCCESS ); +} + +/* +================== +IRC_ProcessData + +Attempts to receive data from the server. If data is received, parse it +and attempt to execute a handler for each complete message. +================== +*/ +static int IRC_ProcessData(void) { + char buffer[ IRC_RECV_BUF_SIZE ]; + int i , len , err_code; + + len = recv( IRC_Socket, buffer, IRC_RECV_BUF_SIZE, 0 ); + + // Handle errors / remote disconnects + if ( len <= 0 ) { + if ( len < 0 ) + IRC_HandleError( ); + IRC_ThreadStatus = IRC_THREAD_QUITTING; + return IRC_CMD_RETRY; + } + + for ( i = 0 ; i < len ; i ++ ) { + if ( IRC_Parser( buffer[ i ] ) ) { +#ifdef DEBUG_DUMP_IRC + IRC_DumpMessage( ); +#endif // DEBUG_DUMP_IRC + err_code = IRC_ExecuteHandler( ); + if ( err_code != IRC_CMD_SUCCESS ) + return err_code; + } + } + + return IRC_CMD_SUCCESS; +} + +/* +================== +IRC_InitialiseUser + +Prepares the user record which is used when issuing the USER command. +================== +*/ +static qboolean IRC_InitialiseUser( const char * name ) { + qboolean ovrnn; + const char * source; + int i = 0, j = 0; + int replaced = 0; + char c; + + ovrnn = cl_IRC_override_nickname->integer && strlen( cl_IRC_nickname->name ); + source = ovrnn ? cl_IRC_nickname->string : name; + + // Strip color chars for the player's name, and remove special + // characters + IRC_User.nicklen = 0; + IRC_User.nickattempts = 1; + while ( j < 15 ) { + if ( !ovrnn ) { + // Only process color escape codes if the nickname + // is being computed from the player source + if ( i == 32 || !source[i] ) { + IRC_User.nick[j ++] = 0; + continue; + } + if ( source[i] == Q_COLOR_ESCAPE ) { + i ++; + if ( source[i] != Q_COLOR_ESCAPE ) { + if ( source[i] ) + i ++; + continue; + } + } + } + + c = source[i ++]; + if ( j == 0 && !( IS_ALPHA( c ) || strchr( "[]\\`_^{|}" , c ) ) ) { + c = '_'; + replaced ++; + } else if ( j > 0 && !( IS_ALNUM( c ) || strchr( "-[]\\`_^{|}" , c ) ) ) { + c = '_'; + replaced ++; + } + IRC_User.nick[j] = c; + + // User names are even more sensitive + if ( ! ( c == '-' || c == '.' || c == '_' || IS_ALNUM( c ) ) ) + c = '_'; + IRC_User.username[j] = c; + + IRC_User.nicklen = ++j; + } + + // If the nickname is overriden and its modified value differs, + // then it is invalid + if ( ovrnn && strcmp( source , IRC_User.nick ) ) + return qfalse; + + // Set static address + strcpy( IRC_User.email, "mymail@mail.com" ); + + return ( IRC_User.nicklen > 0 && replaced < IRC_User.nicklen / 2 ); +} + +/* +================== +IRC_AttemptConnection + +Establishes the IRC connection, sets the nick, etc... +================== +*/ +#define CHECK_SHUTDOWN { if ( IRC_QuitRequested ) return IRC_CMD_FATAL; } +#define CHECK_SHUTDOWN_CLOSE { if ( IRC_QuitRequested ) { closesocket( IRC_Socket ); return IRC_CMD_FATAL; } } + +static int IRC_AttemptConnection() { + struct sockaddr_in address; // socket address + struct hostent * host; // host lookup + char host_name[100]; // host name + char name[32]; // player's name + int err_code; + int port; + + CHECK_SHUTDOWN; + Com_Printf("...IRC: connecting to server\n"); + + // Force players to use a non-default name + strcpy( name, Cvar_VariableString( "name" ) ); + if (! Q_strnicmp( name , "player" , 7 ) ) { + Com_Printf("...IRC: rejected due to unset player name\n"); + return IRC_CMD_FATAL; + } + + // Prepare USER record + if (! IRC_InitialiseUser( name ) ) { + Com_Printf("...IRC: rejected due to mostly unusable player name\n"); + return IRC_CMD_FATAL; + } + + // Find server address + Q_strncpyz2( host_name, cl_IRC_server->string, sizeof(host_name) ); + if ( (host=gethostbyname(host_name)) == NULL ) { + Com_Printf("...IRC: unknown server\n"); + return IRC_CMD_FATAL; + } + + // Create socket + CHECK_SHUTDOWN; + if ( (IRC_Socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET ) { + IRC_HandleError( ); + return IRC_CMD_FATAL; + } + + // Initialise socket address + port = cl_IRC_port->integer; + if ( port <= 0 || port >= 65536 ) { + Com_Printf("IRC: invalid port number, defaulting to 6667\n"); + port = 6667; + } + address.sin_family = AF_INET; + address.sin_port = htons( port ); + address.sin_addr.s_addr = *((unsigned long *) host->h_addr); + + // Attempt connection + if ( (connect(IRC_Socket,(struct sockaddr *) &address, sizeof(address))) != 0) { + closesocket(IRC_Socket); + Com_Printf("...IRC connection refused.\n"); + return IRC_CMD_RETRY; + } + + // Send username and nick name + CHECK_SHUTDOWN_CLOSE; + err_code = IRC_Send( "USER %s %s %s :%s\n" , IRC_User.username , IRC_User.email , host_name , IRC_User.nick ); + if ( err_code == IRC_CMD_SUCCESS ) + err_code = IRC_SendNickname( ); + if ( err_code != IRC_CMD_SUCCESS ) { + closesocket(IRC_Socket); + return err_code; + } + + // Initialise parser and set thread state + IRC_ParserState = IRC_PARSER_START; + IRC_ThreadStatus = IRC_THREAD_SETNICK; + + CHECK_SHUTDOWN_CLOSE; + Com_Printf("...Connected to IRC server\n"); + return IRC_CMD_SUCCESS; +} + +/* +================== +IRC_InitialConnect + +Attempt to connect to the IRC server for the first time. +Only retry a few times and assume the server's dead/does not exist if +connection can't be established. +================== +*/ +static qboolean IRC_InitialConnect( ) { + int err_code , retries = 3; + int rc_delay = cl_IRC_reconnect_delay->integer; + if ( rc_delay < 5 ) + rc_delay = 5; + + err_code = IRC_CMD_SUCCESS; + IRC_ThreadStatus = IRC_THREAD_CONNECTING; + do { + // If we're re-attempting a connection, wait a little bit, + // or we might just piss the server off. + if ( err_code == IRC_CMD_RETRY ) { + IRC_Sleep( rc_delay ); + } else if ( IRC_QuitRequested ) { + return qfalse; + } + + err_code = IRC_AttemptConnection( ); + } while ( err_code == IRC_CMD_RETRY && --retries > 0 ); + + return ( err_code == IRC_CMD_SUCCESS ); +} + +/* +================== +IRC_Reconnect + +Attempt to reconnect to the IRC server. Only stop trying on fatal errors +or if the thread's status is set to QUITTING. +================== +*/ +static int IRC_Reconnect( ) { + int err_code; + int rc_delay = cl_IRC_reconnect_delay->integer; + if ( rc_delay < 5 ) + rc_delay = 5; + + err_code = IRC_CMD_SUCCESS; + IRC_ThreadStatus = IRC_THREAD_CONNECTING; + do { + IRC_Sleep( ( err_code == IRC_CMD_SUCCESS ) ? ( rc_delay >> 1 ) : rc_delay ); + if ( IRC_QuitRequested ) { + return IRC_CMD_FATAL; + } + err_code = IRC_AttemptConnection( ); + } while ( err_code == IRC_CMD_RETRY ); + + return err_code; +} + +/* +================== +IRC_MainLoop + +Once the initial connection has been established, either +1) pump messages or 2) handle delayed functions. Try re-connecting if +connection is lost. +================== +*/ +static void IRC_MainLoop() { + int err_code; + + // Connect to server + if (! IRC_InitialConnect() ) + return; + + do { + do { + // If we must quit, send the command. + if ( IRC_QuitRequested && IRC_ThreadStatus != IRC_THREAD_QUITTING ) { + IRC_ThreadStatus = IRC_THREAD_QUITTING; + IRC_Display( IRC_MakeEvent(QUIT,1) , "" , "quit from menu\n" ); + err_code = IRC_Send( "QUIT :OpenWolf IRC %s\n" , Q3_VERSION ); + } else { + // Wait for data or 1s timeout + err_code = IRC_Wait( ); + if ( err_code == IRC_CMD_SUCCESS ) { + // We have some data, process it + err_code = IRC_ProcessData( ); + } else if ( err_code == IRC_CMD_RETRY ) { + // Timed out, handle timers and update rate limiter + err_code = IRC_ProcessDEQueue(); + IRC_UpdateRateLimiter( ); + } else { + // Disconnected, but reconnection should be attempted + err_code = IRC_CMD_RETRY; + } + + if ( err_code == IRC_CMD_SUCCESS && ! IRC_QuitRequested ) + err_code = IRC_ProcessSendQueue( ) ? IRC_CMD_SUCCESS : IRC_CMD_RETRY; + } + } while ( err_code == IRC_CMD_SUCCESS ); + closesocket( IRC_Socket ); + + // If we must quit, let's skip trying to reconnect + if ( IRC_QuitRequested || err_code == IRC_CMD_FATAL ) + return; + + // Reconnect to server + do { + err_code = IRC_Reconnect( ); + } while ( err_code == IRC_CMD_RETRY ); + } while ( err_code != IRC_CMD_FATAL ); +} + +/* +================== +IRC_Thread + +Main function of the IRC thread: initialise command handlers, +start the main loop, and uninitialise handlers after the loop +exits. +================== +*/ +static void IRC_Thread( ) { + // Init. send queue & rate limiter + IRC_InitSendQueue( ); + IRC_InitRateLimiter( ); + IRC_InitHandlers( ); + + // Init. IRC handlers + IRC_AddHandler( "PING" , &IRCH_Ping ); // Ping request + IRC_AddHandler( "ERROR" , &IRCH_ServerError ); // Server error + IRC_AddHandler( "JOIN" , &IRCH_Joined ); // Channel join + IRC_AddHandler( "PART" , &IRCH_Part ); // Channel part + IRC_AddHandler( "QUIT" , &IRCH_Quit ); // Client quit + IRC_AddHandler( "PRIVMSG" , &IRCH_PrivMsg ); // Message or CTCP + IRC_AddHandler( "KICK" , &IRCH_Kick ); // Kick + IRC_AddHandler( "NICK" , &IRCH_Nick ); // Nick change + IRC_AddHandler( "001" , &IRCH_Connected ); // Connection established + IRC_AddHandler( "404" , &IRCH_Banned ); // Banned (when sending message) + IRC_AddHandler( "432" , &IRCH_FatalError ); // Erroneous nick name + IRC_AddHandler( "433" , &IRCH_NickError ); // Nick name in use + IRC_AddHandler( "474" , &IRCH_Banned ); // Banned (when joining) + + // Init. CTCP handlers + IRC_AddCTCPHandler( "ACTION" , &CTCP_Action ); // "/me" + IRC_AddCTCPHandler( "PING" , &CTCP_Ping ); + IRC_AddCTCPHandler( "VERSION" , &CTCP_Version ); + + // Enter loop + IRC_MainLoop( ); + + // Clean up + Com_Printf( "...IRC: disconnected from server\n" ); + IRC_FlushDEQueue( ); + IRC_FreeHandlers( ); + IRC_SetThreadDead( ); +} + +/* + * Caution: IRC_SystemThreadProc(), IRC_StartThread() and IRC_WaitThread() + * have separate "VARIANTS". + * + * Note different prototypes for IRC_SystemThreadProc() and completely + * different IRC_StartThread()/IRC_WaitThread() implementations. + */ +#ifdef WIN32 + +/****** THREAD HANDLING - WINDOWS VARIANT ******/ + +static HANDLE IRC_ThreadHandle = NULL; +/* +================== +IRC_SystemThreadProc +================== +*/ +static DWORD WINAPI IRC_SystemThreadProc( LPVOID dummy) { + IRC_Thread( ); + return 0; +} + +/* +================== +IRC_StartThread +================== +*/ +static void IRC_StartThread() { + if ( IRC_ThreadHandle == NULL ) + IRC_ThreadHandle = CreateThread( NULL , 0 , IRC_SystemThreadProc , NULL , 0 , NULL ); +} + +/* +================== +IRC_SetThreadDead +================== +*/ +static void IRC_SetThreadDead( ) { + IRC_ThreadStatus = IRC_THREAD_DEAD; + IRC_ThreadHandle = NULL; +} + +/* +================== +IRC_StartThread +================== +*/ +static void IRC_WaitThread() { + if ( IRC_ThreadHandle != NULL ) { + if ( IRC_ThreadStatus != IRC_THREAD_DEAD ) { + WaitForSingleObject( IRC_ThreadHandle , 10000 ); + CloseHandle( IRC_ThreadHandle ); + } + IRC_ThreadHandle = NULL; + } +} + +#elif defined __linux__ || defined MACOS_X || defined __FreeBSD__ + +/****** THREAD HANDLING - UNIX VARIANT ******/ + +static pthread_t IRC_ThreadHandle = (pthread_t) NULL; +/* +================== +IRC_SystemThreadProc +================== +*/ +static void *IRC_SystemThreadProc(void *dummy) { + IRC_Thread( ); + return NULL; +} + +/* +================== +IRC_StartThread +================== +*/ +static void IRC_StartThread(void) { + if ( IRC_ThreadHandle == (pthread_t) NULL ) + pthread_create( &IRC_ThreadHandle , NULL , IRC_SystemThreadProc , NULL ); +} + +/* +================== +IRC_SetThreadDead +================== +*/ +static void IRC_SetThreadDead( ) { + IRC_ThreadStatus = IRC_THREAD_DEAD; + IRC_ThreadHandle = (pthread_t) NULL; +} + +/* +================== +IRC_WaitThread +================== +*/ +static void IRC_WaitThread() { + if ( IRC_ThreadHandle != (pthread_t) NULL ) { + if ( IRC_ThreadStatus != IRC_THREAD_DEAD ) + pthread_join( IRC_ThreadHandle , NULL ); + IRC_ThreadHandle = (pthread_t) NULL; + } +} + +#endif + +/* +================== +CL_IRCSetup +================== +*/ +void CL_OW_IRCSetup(void) { + cl_IRC_connect_at_startup = Cvar_Get( "cl_IRC_connect_at_startup" , "1" , CVAR_ARCHIVE ); + cl_IRC_server = Cvar_Get( "cl_IRC_server" , "irc.freenode.org" , CVAR_ARCHIVE ); + cl_IRC_channel = Cvar_Get( "cl_IRC_channel" , "openwolf" , CVAR_ARCHIVE ); + cl_IRC_port = Cvar_Get( "cl_IRC_port" , "6667" , CVAR_ARCHIVE ); + cl_IRC_override_nickname = Cvar_Get( "cl_IRC_override_nickname" , "0" , CVAR_ARCHIVE ); + cl_IRC_nickname = Cvar_Get( "cl_IRC_nickname" , "" , CVAR_ARCHIVE ); + cl_IRC_kick_rejoin = Cvar_Get( "cl_IRC_kick_rejoin" , "0" , CVAR_ARCHIVE ); + cl_IRC_reconnect_delay = Cvar_Get( "cl_IRC_reconnect_delay" , "100" , CVAR_ARCHIVE ); + + if ( cl_IRC_connect_at_startup->value ) + CL_OW_InitIRC( ); +} + +/* +================== +CL_InitIRC +================== +*/ +void CL_OW_InitIRC(void) { + if ( IRC_ThreadStatus != IRC_THREAD_DEAD ) { + Com_Printf( "...IRC thread is already running\n" ); + return; + } + IRC_QuitRequested = qfalse; + IRC_ThreadStatus = IRC_THREAD_INITIALISING; + IRC_StartThread( ); +} + +/* +================== +CL_IRCInitiateShutdown +================== +*/ +void CL_OW_IRCInitiateShutdown(void) { + IRC_QuitRequested = qtrue; +} + +/* +================== +CL_IRCWaitShutdown +================== +*/ +void CL_OW_IRCWaitShutdown( void ) { + IRC_WaitThread( ); +} + +/* +================== +CL_IRCIsConnected +================== +*/ +qboolean CL_OW_IRCIsConnected(void) { + // get IRC status + return ( IRC_ThreadStatus == IRC_THREAD_JOINED ); +} + + +/* +================== +CL_IRCIsRunning +================== +*/ +qboolean CL_OW_IRCIsRunning(void) { + // return IRC status + return ( IRC_ThreadStatus != IRC_THREAD_DEAD ); +} diff --git a/src/engine/client/cl_keys.c b/src/engine/client/cl_keys.c new file mode 100644 index 0000000000..2017b3b2cf --- /dev/null +++ b/src/engine/client/cl_keys.c @@ -0,0 +1,1642 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +#include "client.h" + +/* + +key up events are sent even if in console mode + +*/ + +field_t g_consoleField; +field_t chatField; +qboolean chat_team; +qboolean chat_buddy; +//Dushan +qboolean chat_irc; + +qboolean key_overstrikeMode; + +int anykeydown; +qkey_t keys[MAX_KEYS]; + + +typedef struct { + char *name; + int keynum; +} keyname_t; + +qboolean UI_checkKeyExec( int key ); // NERVE - SMF +qboolean CL_CGameCheckKeyExec( int key ); + +// 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}, + + {"XBOX360_A", K_XBOX360_A}, + {"XBOX360_B", K_XBOX360_B}, + {"XBOX360_X", K_XBOX360_X}, + {"XBOX360_Y", K_XBOX360_Y}, + {"XBOX360_LB", K_XBOX360_LB}, + {"XBOX360_RB", K_XBOX360_RB}, + {"XBOX360_START", K_XBOX360_START}, + {"XBOX360_GUIDE", K_XBOX360_GUIDE}, + {"XBOX360_LS", K_XBOX360_LS}, + {"XBOX360_RS", K_XBOX360_RS}, + {"XBOX360_BACK", K_XBOX360_BACK}, + {"XBOX360_LT", K_XBOX360_LT}, + {"XBOX360_RT", K_XBOX360_RT}, + {"XBOX360_DPAD_UP", K_XBOX360_DPAD_UP}, + {"XBOX360_DPAD_RIGHT", K_XBOX360_DPAD_RIGHT}, + {"XBOX360_DPAD_DOWN", K_XBOX360_DPAD_DOWN}, + {"XBOX360_DPAD_LEFT", K_XBOX360_DPAD_LEFT}, + {"XBOX360_DPAD_RIGHTUP", K_XBOX360_DPAD_RIGHTUP}, + {"XBOX360_DPAD_RIGHTDOWN", K_XBOX360_DPAD_RIGHTDOWN}, + {"XBOX360_DPAD_LEFTUP", K_XBOX360_DPAD_LEFTUP}, + {"XBOX360_DPAD_LEFTDOWN", K_XBOX360_DPAD_LEFTDOWN}, + + {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 size, qboolean showCursor, + qboolean noColorEscape, float alpha ) { + 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] = 1.0; + color[3] = alpha; + 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 ) { + float xlocation = x + SCR_ConsoleFontStringWidth( str + prestep, edit->cursor - prestep) ; + SCR_DrawConsoleFontChar( xlocation , 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, qboolean showCursor, qboolean noColorEscape, float alpha ) +{ + Field_VariableSizeDraw( edit, x, y, SMALLCHAR_WIDTH, showCursor, noColorEscape, alpha ); +} + +void Field_BigDraw( field_t *edit, int x, int y, qboolean showCursor, qboolean noColorEscape ) +{ + Field_VariableSizeDraw( edit, x, y, BIGCHAR_WIDTH, showCursor, noColorEscape, 1.0f ); +} + +/* +================ +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; + } + + len = strlen( edit->buffer ); + + switch ( key ) { + case K_DEL: + case K_KP_DEL: + if ( edit->cursor < len ) { + memmove( edit->buffer + edit->cursor, + edit->buffer + edit->cursor + 1, len - edit->cursor ); + } + break; + + case K_RIGHTARROW: + case K_KP_RIGHTARROW: + if ( edit->cursor < len ) { + edit->cursor++; + } + break; + + case K_LEFTARROW: + case K_KP_LEFTARROW: + if ( edit->cursor > 0 ) { + edit->cursor--; + } + break; + case K_HOME: + case K_KP_HOME: + edit->cursor = 0; + case 'a': + if ( keys[K_CTRL].down ) { + edit->cursor = 0; + } + break; + case K_END: + case K_KP_END: + edit->cursor = len; + case 'e': + if ( keys[K_CTRL].down ) { + edit->cursor = len; + } + break; + case K_INS: + case K_KP_INS: + key_overstrikeMode = !key_overstrikeMode; + 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 + +============================================================================== +*/ + +static char completionString[MAX_TOKEN_CHARS]; +static char currentMatch[MAX_TOKEN_CHARS]; +static int matchCount; +static int matchIndex; + +/* +=============== +FindMatches + +=============== +*/ +static void FindMatches( const char *s ) { + int i; + + if ( Q_stricmpn( s, completionString, strlen( completionString ) ) ) { + return; + } + matchCount++; + if ( matchCount == 1 ) { + Q_strncpyz( currentMatch, s, sizeof( currentMatch ) ); + return; + } + + // cut currentMatch to the amount common with s + for ( i = 0 ; s[i] ; i++ ) { + if ( tolower( currentMatch[i] ) != tolower( s[i] ) ) { + currentMatch[i] = 0; + } + } + currentMatch[i] = 0; +} + +/* +=============== +FindIndexMatch + +=============== +*/ +static int findMatchIndex; +static void FindIndexMatch( const char *s ) { + + if ( Q_stricmpn( s, completionString, strlen( completionString ) ) ) { + return; + } + + if ( findMatchIndex == matchIndex ) { + Q_strncpyz( currentMatch, s, sizeof( currentMatch ) ); + } + + findMatchIndex++; +} + +/* +=============== +PrintMatches + +=============== +*/ +static void PrintMatches( const char *s ) { + if ( !Q_stricmpn( s, currentMatch, strlen( currentMatch ) ) ) { + Com_Printf( " ^9%s^0\n", s ); + } +} + +// ydnar: to display cvar values +static void PrintCvarMatches( const char *s ) { + if ( !Q_stricmpn( s, currentMatch, strlen( currentMatch ) ) ) { + Com_Printf( " ^9%s = ^5%s^0\n", s, Cvar_VariableString( s ) ); + } +} + +static void keyConcatArgs( void ) { + int i; + char *arg; + + for ( i = 1 ; i < Cmd_Argc() ; i++ ) { + Q_strcat( g_consoleField.buffer, sizeof( g_consoleField.buffer ), " " ); + arg = Cmd_Argv( i ); + while ( *arg ) { + if ( *arg == ' ' ) { + Q_strcat( g_consoleField.buffer, sizeof( g_consoleField.buffer ), "\"" ); + break; + } + arg++; + } + Q_strcat( g_consoleField.buffer, sizeof( g_consoleField.buffer ), Cmd_Argv( i ) ); + if ( *arg == ' ' ) { + Q_strcat( g_consoleField.buffer, sizeof( g_consoleField.buffer ), "\"" ); + } + } +} + +static void ConcatRemaining( const char *src, const char *start ) { + char *str; + + str = strstr( src, start ); + if ( !str ) { + keyConcatArgs(); + return; + } + + str += strlen( start ); + Q_strcat( g_consoleField.buffer, sizeof( g_consoleField.buffer ), str ); +} + + +/* +=============== +CompleteCommand + +Tab expansion +=============== +*/ +static void CompleteCommand( void ) { + field_t *edit; + edit = &g_consoleField; + + // only look at the first token for completion purposes + Cmd_TokenizeString( edit->buffer ); + + Field_AutoComplete( edit, "]" ); +} + + +/* +==================== +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 ) { + con.acLength = 0; + + // 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_STRING_CHARS]; + + 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 + Hist_Add( g_consoleField.buffer ); + if ( cls.state == CA_DISCONNECTED ) { + SCR_UpdateScreen(); // force an update, because the command + } // may take some time + Field_Clear( &g_consoleField ); + return; + } + + // command completion + + if ( key == K_TAB ) { + CompleteCommand(); + return; + } + + // clear autocompletion buffer on normal key input + if ( ( key >= K_SPACE && key <= K_BACKSPACE ) || ( key == K_LEFTARROW ) || ( key == K_RIGHTARROW ) || + ( key >= K_KP_LEFTARROW && key <= K_KP_RIGHTARROW ) || + ( key >= K_KP_SLASH && key <= K_KP_PLUS ) || ( key >= K_KP_STAR && key <= K_KP_EQUALS ) ) { + con.acLength = 0; + } + + + // command history (ctrl-p ctrl-n for unix style) + + //----(SA) added some mousewheel functionality to the console + if ( ( key == K_MWHEELUP && keys[K_SHIFT].down ) || ( key == K_UPARROW ) || ( key == K_KP_UPARROW ) || + ( ( tolower( key ) == 'p' ) && keys[K_CTRL].down ) ) { + Q_strncpyz( g_consoleField.buffer, Hist_Prev(), sizeof( g_consoleField.buffer ) ); + g_consoleField.cursor = strlen( g_consoleField.buffer ); + if ( g_consoleField.cursor >= g_consoleField.widthInChars ) { + g_consoleField.scroll = g_consoleField.cursor - g_consoleField.widthInChars + 1; + } else { + g_consoleField.scroll = 0; + } + con.acLength = 0; + return; + } + + //----(SA) added some mousewheel functionality to the console + if ( ( key == K_MWHEELDOWN && keys[K_SHIFT].down ) || ( key == K_DOWNARROW ) || ( key == K_KP_DOWNARROW ) || + ( ( tolower( key ) == 'n' ) && keys[K_CTRL].down ) ) { + const char *history = Hist_Next(); + if ( history ) { + Q_strncpyz( g_consoleField.buffer, history, sizeof( g_consoleField.buffer ) ); + g_consoleField.cursor = strlen( g_consoleField.buffer ); + if ( g_consoleField.cursor >= g_consoleField.widthInChars ) { + g_consoleField.scroll = g_consoleField.cursor - g_consoleField.widthInChars + 1; + } else { + g_consoleField.scroll = 0; + } + } else if ( g_consoleField.buffer[0] ) { + Hist_Add( g_consoleField.buffer ); + Field_Clear( &g_consoleField ); + } + + con.acLength = 0; + return; + } + + // console scrolling + if ( key == K_PGUP || key == K_KP_PGUP ) { + Con_PageUp(); + return; + } + + if ( key == K_PGDN || key == K_KP_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 || key == K_KP_HOME ) && keys[K_CTRL].down ) { + Con_Top(); + return; + } + + // ctrl-end = bottom of console + if ( ( key == K_END || key == K_KP_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 ) { + cls.keyCatchers &= ~KEYCATCH_MESSAGE; + Field_Clear( &chatField ); + return; + } + + if ( key == K_ENTER || key == K_KP_ENTER ) { + if ( chatField.buffer[0] && cls.state == CA_ACTIVE ) { + if ( chat_team ) { + Com_sprintf( buffer, sizeof( buffer ), "say_team \"%s\"\n", chatField.buffer ); + } else if ( chat_buddy ) { + Com_sprintf( buffer, sizeof( buffer ), "say_buddy \"%s\"\n", chatField.buffer ); + } else { + Com_sprintf( buffer, sizeof( buffer ), "say \"%s\"\n", chatField.buffer ); + } + + CL_AddReliableCommand( buffer ); + } + cls.keyCatchers &= ~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; +} + +#define BIND_HASH_SIZE 1024 + +static long generateHashValue( const char *fname ) { + int i; + long hash; + + if ( !fname ) { + return 0; + } + + hash = 0; + i = 0; + while ( fname[i] != '\0' ) { + hash += (long)( fname[i] ) * ( i + 119 ); + i++; + } + hash &= ( BIND_HASH_SIZE - 1 ); + return hash; +} + +/* +=================== +Key_SetBinding +=================== +*/ +void Key_SetBinding( int keynum, const char *binding ) { + + char *lcbinding; // fretn - make a copy of our binding lowercase + // so name toggle scripts work again: bind x name BzZIfretn? + // resulted into bzzifretn? + + 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 ); + lcbinding = CopyString( binding ); + Q_strlwr( lcbinding ); // saves doing it on all the generateHashValues in Key_GetBindingByString + + keys[keynum].hash = generateHashValue( lcbinding ); + + // 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; +} + +// binding MUST be lower case +void Key_GetBindingByString( const char* binding, int* key1, int* key2 ) { + int i; + int hash = generateHashValue( binding ); + + *key1 = -1; + *key2 = -1; + + for ( i = 0; i < MAX_KEYS; i++ ) { + if ( keys[i].hash == hash && !Q_stricmp( binding, keys[i].binding ) ) { + if ( *key1 == -1 ) { + *key1 = i; + } else if ( *key2 == -1 ) { + *key2 = i; + return; + } + } + } +} + +/* +=================== +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 +==================== +*/ +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_KeyEvent + +Called by the system for both key up and key down events +=================== +*/ +//static consoleCount = 0; +// fretn +qboolean consoleButtonWasPressed = qfalse; + +void CL_KeyEvent( int key, qboolean down, unsigned time ) { + char *kb; + char cmd[1024]; + qboolean bypassMenu = qfalse; // NERVE - SMF + qboolean onlybinds = qfalse; + + if ( !key ) { + return; + } + + + switch ( key ) { + case K_KP_PGUP: + case K_KP_EQUALS: + case K_KP_5: + case K_KP_LEFTARROW: + case K_KP_UPARROW: + case K_KP_RIGHTARROW: + case K_KP_DOWNARROW: + case K_KP_END: + case K_KP_PGDN: + case K_KP_INS: + case K_KP_DEL: + case K_KP_HOME: + if ( Sys_IsNumLockDown() ) { + onlybinds = qtrue; + } + break; + } + + + // 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; + } + } + } + +#ifdef MACOS_X + if ( down && keys[ K_COMMAND ].down ) { + + if ( key == 'f' ) { + Key_ClearStates(); + Cbuf_ExecuteText( EXEC_APPEND, "toggle r_fullscreen\nvid_restart\n" ); + return; + } else if ( key == 'q' ) { + Key_ClearStates(); + Cbuf_ExecuteText( EXEC_APPEND, "quit\n" ); + return; + } + } +#endif + + // console key is hardcoded, so the user can never unbind it + if( key == K_CONSOLE || ( keys[K_SHIFT].down && key == K_ESCAPE ) ) { + if(!down) + { + return; + } + Con_ToggleConsole_f(); + Key_ClearStates(); + return; + } + +//----(SA) added + if ( cl.cameraMode ) { + if ( !( cls.keyCatchers & ( KEYCATCH_UI | KEYCATCH_CONSOLE ) ) ) { // let menu/console handle keys if necessary + + // in cutscenes we need to handle keys specially (pausing not allowed in camera mode) + if ( ( key == K_ESCAPE || + key == K_SPACE || + key == K_ENTER ) && down ) { + CL_AddReliableCommand( "cameraInterrupt" ); + return; + } + + // eat all keys + if ( down ) { + return; + } + } + + if ( ( cls.keyCatchers & KEYCATCH_CONSOLE ) && key == K_ESCAPE ) { + // don't allow menu starting when console is down and camera running + return; + } + } + //----(SA) end + + // most keys during demo playback will bring up the menu, but non-ascii + + // keys can still be used for bound actions + if ( down && ( key < 128 || key == K_MOUSE1 ) + && ( clc.demoplaying || cls.state == CA_CINEMATIC ) && !cls.keyCatchers ) { + + Cvar_Set( "nextdemo","" ); + key = K_ESCAPE; + } + + // escape is always handled special + if ( key == K_ESCAPE && down ) { + if ( cls.keyCatchers & KEYCATCH_MESSAGE ) { + // clear message mode + Message_Key( key ); + return; + } + + // escape always gets out of CGAME stuff + if ( cls.keyCatchers & KEYCATCH_CGAME ) { + cls.keyCatchers &= ~KEYCATCH_CGAME; + VM_Call( cgvm, CG_EVENT_HANDLING, CGAME_EVENT_NONE ); + return; + } + + if ( !( cls.keyCatchers & KEYCATCH_UI ) ) { + if ( cls.state == CA_ACTIVE && !clc.demoplaying ) { + // Arnout: on request + if ( cls.keyCatchers & KEYCATCH_CONSOLE ) { // get rid of the console + Con_ToggleConsole_f(); + } else { + VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_INGAME ); + } + } else { + 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 ) { + kb = keys[key].binding; + if ( kb && kb[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", kb + 1, key, time ); + Cbuf_AddText( cmd ); + } + + if ( cls.keyCatchers & KEYCATCH_UI && uivm ) { + if ( !onlybinds || VM_Call( uivm, UI_WANTSBINDKEYS ) ) { + VM_Call( uivm, UI_KEY_EVENT, key, down ); + } + } else if ( cls.keyCatchers & KEYCATCH_CGAME && cgvm ) { + if ( !onlybinds || VM_Call( cgvm, CG_WANTSBINDKEYS ) ) { + VM_Call( cgvm, CG_KEY_EVENT, key, down ); + } + } + + return; + } + + // NERVE - SMF - if we just want to pass it along to game + if ( cl_bypassMouseInput && cl_bypassMouseInput->integer ) { + if ( ( key == K_MOUSE1 || key == K_MOUSE2 || key == K_MOUSE3 || key == K_MOUSE4 || key == K_MOUSE5 ) ) { + if ( cl_bypassMouseInput->integer == 1 ) { + bypassMenu = qtrue; + } + } else if ( ( cls.keyCatchers & KEYCATCH_UI && !UI_checkKeyExec( key ) ) || ( cls.keyCatchers & KEYCATCH_CGAME && !CL_CGameCheckKeyExec( key ) ) ) { + bypassMenu = qtrue; + } + } + + // distribute the key down event to the apropriate handler + if ( cls.keyCatchers & KEYCATCH_CONSOLE ) { + if ( !onlybinds ) { + Console_Key( key ); + } + } else if ( cls.keyCatchers & KEYCATCH_UI && !bypassMenu ) { + if ( !onlybinds || VM_Call( uivm, UI_WANTSBINDKEYS ) ) { + VM_Call( uivm, UI_KEY_EVENT, key, down ); + } + } else if ( cls.keyCatchers & KEYCATCH_CGAME && !bypassMenu ) { + if ( cgvm ) { + if ( !onlybinds || VM_Call( cgvm, CG_WANTSBINDKEYS ) ) { + VM_Call( cgvm, CG_KEY_EVENT, key, down ); + } + } + } else if ( cls.keyCatchers & KEYCATCH_MESSAGE ) { + if ( !onlybinds ) { + Message_Key( key ); + } + } else if ( cls.state == CA_DISCONNECTED ) { + + if ( !onlybinds ) { + 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] == '+' ) { + // 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", kb, key, time ); + Cbuf_AddText( cmd ); + } else { + // down-only command + Cbuf_AddText( kb ); + Cbuf_AddText( "\n" ); + } + } +} + + +/* +=================== +CL_CharEvent + +Normal keyboard characters, already shifted / capslocked / etc +=================== +*/ +void CL_CharEvent( int key ) { + // the console key should never be used as a char + // ydnar: added uk equivalent of shift+` + // the RIGHT way to do this would be to have certain keys disable the equivalent SE_CHAR event + + // fretn - this should be fixed in Com_EventLoop + // but I can't be arsed to leave this as is + + if ( key == (unsigned char) '`' || key == (unsigned char) '~' || key == (unsigned char) '¬' ) { + return; + } + + // distribute the key down event to the apropriate handler + if ( cls.keyCatchers & KEYCATCH_CONSOLE ) { + Field_CharEvent( &g_consoleField, key ); + } else if ( cls.keyCatchers & KEYCATCH_UI ) { + VM_Call( uivm, UI_KEY_EVENT, key | K_CHAR_FLAG, qtrue ); + } else if ( cls.keyCatchers & KEYCATCH_CGAME ) { + VM_Call( cgvm, CG_KEY_EVENT, key | K_CHAR_FLAG, qtrue ); + } else if ( cls.keyCatchers & 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; + } +} diff --git a/src/engine/client/cl_main.c b/src/engine/client/cl_main.c new file mode 100644 index 0000000000..3c7c0f4bab --- /dev/null +++ b/src/engine/client/cl_main.c @@ -0,0 +1,7122 @@ +/* +=========================================================================== + +OpenWolf GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the OpenWolf GPL Source Code (OpenWolf Source Code). + +OpenWolf Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenWolf 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 OpenWolf Source Code. If not, see . + +In addition, the OpenWolf Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the OpenWolf +Source Code. If not, please request a copy in writing from id Software at the address +below. + +If you have questions concerning this license or the applicable additional terms, you +may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, +Maryland 20850 USA. + +=========================================================================== +*/ + +// cl_main.c -- client main loop + +#include "client.h" +#include +#include +#include + +#ifdef USE_RUBY +#include "ruby.h" +#undef vsnprintf +#define vsnprintf vsnprintf +#endif + +#ifdef ET_MYSQL +#include "../database/database.h" +#endif + +#include "snd_local.h" // fretn + +#include "../sys/sys_loadlib.h" +#include "../sys/sys_local.h" + +cvar_t *cl_wavefilerecord; + +#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_voipShowSender; +cvar_t *cl_voipSenderPos; +cvar_t *cl_voip; +#endif + +#ifdef USE_RUBY +cvar_t *cl_ruby; +#endif + +cvar_t *cl_nodelta; +cvar_t *cl_debugMove; + +cvar_t *cl_noprint; +cvar_t *cl_motd; +cvar_t *cl_autoupdate; // DHM - Nerve + +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 = NULL; // NERVE - SMF - This is referenced in msg.c and we need to make sure it is NULL +cvar_t *cl_shownuments; // DHM - Nerve +cvar_t *cl_visibleClients; // DHM - Nerve +cvar_t *cl_showSend; +cvar_t *cl_showServerCommands; // NERVE - SMF +cvar_t *cl_timedemo; + +cvar_t *cl_aviFrameRate; +cvar_t *cl_forceavidemo; + +cvar_t *cl_freelook; +cvar_t *cl_sensitivity; +cvar_t *cl_xbox360ControllerAvailable; + +cvar_t *cl_mouseAccelOffset; +cvar_t *cl_mouseAccel; +cvar_t *cl_mouseAccelStyle; +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 *j_pitch; +cvar_t *j_yaw; +cvar_t *j_forward; +cvar_t *j_side; +cvar_t *j_pitch_axis; +cvar_t *j_yaw_axis; +cvar_t *j_forward_axis; +cvar_t *j_side_axis; + +cvar_t *cl_activeAction; + +cvar_t *cl_autorecord; + +cvar_t *cl_motdString; + +cvar_t *cl_allowDownload; +cvar_t *cl_wwwDownload; +cvar_t *cl_conXOffset; +cvar_t *cl_inGameVideo; + +cvar_t *cl_serverStatusResendTime; +cvar_t *cl_trn; +cvar_t *cl_missionStats; +cvar_t *cl_waitForFire; + +// NERVE - SMF - localization +cvar_t *cl_language; +cvar_t *cl_debugTranslation; + +// -NERVE - SMF +// DHM - Nerve :: Auto-Update +cvar_t *cl_updateavailable; +cvar_t *cl_updatefiles; + +cvar_t *cl_pubkeyID; + +// DHM - Nerve + +cvar_t *cl_authserver; + +cvar_t *cl_profile; +cvar_t *cl_defaultProfile; + +cvar_t *cl_demorecording; // fretn +cvar_t *cl_demofilename; // bani +cvar_t *cl_demooffset; // bani + +cvar_t *cl_waverecording; //bani +cvar_t *cl_wavefilename; //bani +cvar_t *cl_waveoffset; //bani + +cvar_t *cl_packetloss; //bani +cvar_t *cl_packetdelay; //bani +extern qboolean sv_cheats; //bani + +cvar_t *cl_consoleKeys; +cvar_t *cl_consoleFont; +cvar_t *cl_consoleFontSize; +cvar_t *cl_consoleFontKerning; +cvar_t *cl_consolePrompt; + +#ifdef USE_CRYPTO +struct rsa_public_key public_key; +struct rsa_private_key private_key; +#endif + +cvar_t *cl_gamename; + +// XreaL BEGIN +cvar_t *cl_aviMotionJpeg; +// XreaL END + +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; + +// DHM - Nerve :: Have we heard from the auto-update server this session? +qboolean autoupdateChecked; +qboolean autoupdateStarted; + +// TTimo : moved from char* to array (was getting the char* from va(), broke on big downloads) +char autoupdateFilename[MAX_QPATH]; + +// "updates" shifted from -7 +#define AUTOUPDATE_DIR "ni]Zm^l" +#define AUTOUPDATE_DIR_SHIFT 7 + +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); +void CL_SaveTranslations_f(void); +void CL_LoadTranslations_f(void); + +// fretn +void CL_WriteWaveClose(void); +void CL_WavStopRecord_f(void); + +void CL_PurgeCache( void ) { + cls.doCachePurge = qtrue; +} + +void CL_DoPurgeCache( void ) { + if ( !cls.doCachePurge ) { + return; + } + + cls.doCachePurge = qfalse; + + if ( !com_cl_running ) { + return; + } + + if ( !com_cl_running->integer ) { + return; + } + + if ( !cls.rendererStarted ) { + return; + } + + re.purgeCache(); +} + +#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); + return; + } + } + Com_Printf("VoIP: invalid player ID#\n"); +} + +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 (!clc.voipEnabled) + reason = "Server doesn't support VoIP"; + + 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) { + if (Cmd_Argc() > 3) { + CL_UpdateVoipGain(Cmd_Argv(2), atof(Cmd_Argv(3))); + } else if (Q_isanumber(Cmd_Argv(2))) { + int id = atoi(Cmd_Argv(2)); + if (id >= 0 && id < MAX_CLIENTS) { + Com_Printf("VoIP: current gain for player #%d " + "is %f\n", id, clc.voipGain[id]); + } else { + Com_Printf("VoIP: invalid player ID#\n"); + } + } else { + Com_Printf("usage: voip gain [value]\n"); + } + } 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; + } else { + Com_Printf("usage: voip [un]ignore \n" + " voip [un]muteall\n" + " voip gain [value]\n"); + } +} + + +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_VoipParseTargets + +sets clc.voipTargets according to cl_voipSendTarget +Generally we don't want who's listening to change during a transmission, +so this is only called when the key is first pressed +=============== +*/ +void CL_VoipParseTargets(void) +{ + const char *target = cl_voipSendTarget->string; + char *end; + int val; + + Com_Memset(clc.voipTargets, 0, sizeof(clc.voipTargets)); + clc.voipFlags &= ~VOIP_SPATIAL; + + while(target) + { + while(*target == ',' || *target == ' ') + target++; + + if(!*target) + break; + + if(isdigit(*target)) + { + val = strtol(target, &end, 10); + target = end; + } + else + { + if(!Q_stricmpn(target, "all", 3)) + { + Com_Memset(clc.voipTargets, ~0, sizeof(clc.voipTargets)); + return; + } + if(!Q_stricmpn(target, "spatial", 7)) + { + clc.voipFlags |= VOIP_SPATIAL; + target += 7; + continue; + } + else + { + if(!Q_stricmpn(target, "attacker", 8)) + { + val = VM_Call(cgvm, CG_LAST_ATTACKER); + target += 8; + } + else if(!Q_stricmpn(target, "crosshair", 9)) + { + val = VM_Call(cgvm, CG_CROSSHAIR_PLAYER); + target += 9; + } + else + { + while(*target && *target != ',' && *target != ' ') + target++; + + continue; + } + + if(val < 0) + continue; + } + } + + if(val < 0 || val >= MAX_CLIENTS) + { + Com_Printf(S_COLOR_YELLOW "WARNING: VoIP " + "target %d is not a valid client " + "number\n", val); + + continue; + } + + clc.voipTargets[val / 8] |= 1 << (val % 8); + } +} + +/* +=============== +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 (!clc.voipEnabled) + dontCapture = qtrue; // server doesn't support VoIP. + 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) + { + Cvar_Set("cl_voipSend", "0"); + return; + } + + if (cl_voipSend->integer) { + initialFrame = qtrue; + } else { + finalFrame = qtrue; + } + } + + // try to get more audio data from the sound card... + + if (initialFrame) { + S_MasterGain(Com_Clamp(0.0f, 1.0f, cl_voipGainDuringCapture->value)); + S_StartCapture(); + CL_VoipNewGeneration(); + CL_VoipParseTargets(); + } + + if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio? + int samples = S_AvailableCaptureSamples(); + const int mult = (finalFrame) ? 1 : 4; // 4 == 80ms 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 * 4)) + samples = (clc.speexFrameSize * 4); + + // !!! 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; + + // NOTE TTimo: what is the randomize for? + 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; + Cvar_Set("cl_demorecording", "0"); // fretn + Cvar_Set("cl_demofilename", ""); // bani + Cvar_Set("cl_demooffset", "0"); // bani + Com_Printf("Stopped demo.\n"); +} + +/* +================== +CL_DemoFilename +================== +*/ +void CL_DemoFilename(int number, char *fileName) +{ + if(number < 0 || number > 9999) + { + Com_sprintf(fileName, MAX_OSPATH, "demo9999"); // fretn - removed .tga + return; + } + + Com_sprintf(fileName, MAX_OSPATH, "demo%04i", number); +} + +/* +==================== +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]; + int len; + char *s; + + if(Cmd_Argc() > 2) + { + Com_Printf("record \n"); + return; + } + + if(clc.demorecording) + { + Com_Printf("Already recording.\n"); + return; + } + + if(cls.state != CA_ACTIVE) + { + Com_Printf("You must be in a level to record.\n"); + return; + } + + + // ATVI Wolfenstein Misc #479 - changing this to a warning + // 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, com_protocol->integer); + } + 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, com_protocol->integer); + + len = FS_ReadFile(name, NULL); + if(len <= 0) + { + break; // file doesn't exist + } + } + } + + CL_Record(name); +} + +void CL_Record(const char *name) +{ + int i; + msg_t buf; + byte bufData[MAX_MSGLEN]; + entityState_t *ent; + entityState_t nullstate; + char *s; + int len; + + // 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; + Cvar_Set("cl_demorecording", "1"); // fretn + Q_strncpyz(clc.demoName, demoName, sizeof(clc.demoName)); + Cvar_Set("cl_demofilename", clc.demoName); // bani + Cvar_Set("cl_demooffset", "0"); // bani + + // 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 + 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_DemoCompleted +================= +*/ + +void CL_DemoCompleted(void) +{ + if(cl_timedemo && cl_timedemo->integer) + { + int time; + + time = Sys_Milliseconds() - clc.timeDemoStart; + if(time > 0) + { + Com_Printf("%i frames, %3.1f seconds: %3.1f fps\n", clc.timeDemoFrames, + time / 1000.0, clc.timeDemoFrames * 1000.0 / time); + } + } + + // fretn + if(clc.waverecording) + { + CL_WriteWaveClose(); + clc.waverecording = qfalse; + } + + 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); +} + +/* +==================== + + Wave file saving functions + + FIXME: make this actually work + +==================== +*/ + +/* +================== +CL_DemoFilename +================== +*/ +void CL_WavFilename(int number, char *fileName) +{ + if(number < 0 || number > 9999) + { + Com_sprintf(fileName, MAX_OSPATH, "wav9999"); // fretn - removed .tga + return; + } + + Com_sprintf(fileName, MAX_OSPATH, "wav%04i", number); +} + +typedef struct wav_hdr_s +{ + unsigned int ChunkID; // big endian + unsigned int ChunkSize; // little endian + unsigned int Format; // big endian + + unsigned int Subchunk1ID; // big endian + unsigned int Subchunk1Size; // little endian + unsigned short AudioFormat; // little endian + unsigned short NumChannels; // little endian + unsigned int SampleRate; // little endian + unsigned int ByteRate; // little endian + unsigned short BlockAlign; // little endian + unsigned short BitsPerSample; // little endian + + unsigned int Subchunk2ID; // big endian + unsigned int Subchunk2Size; // little indian ;) + + unsigned int NumSamples; +} wav_hdr_t; + +wav_hdr_t hdr; + +static void CL_WriteWaveHeader(void) +{ + memset(&hdr, 0, sizeof(hdr)); + + hdr.ChunkID = 0x46464952; // "RIFF" + hdr.ChunkSize = 0; // total filesize - 8 bytes + hdr.Format = 0x45564157; // "WAVE" + + hdr.Subchunk1ID = 0x20746d66; // "fmt " + hdr.Subchunk1Size = 16; // 16 = pcm + hdr.AudioFormat = 1; // 1 = linear quantization + hdr.NumChannels = 2; // 2 = stereo + + hdr.SampleRate = dma.speed; + + hdr.BitsPerSample = 16; // 16bits + + // SampleRate * NumChannels * BitsPerSample/8 + hdr.ByteRate = hdr.SampleRate * hdr.NumChannels * (hdr.BitsPerSample / 8); + + // NumChannels * BitsPerSample/8 + hdr.BlockAlign = hdr.NumChannels * (hdr.BitsPerSample / 8); + + hdr.Subchunk2ID = 0x61746164; // "data" + + hdr.Subchunk2Size = 0; // NumSamples * NumChannels * BitsPerSample/8 + + // ... + FS_Write(&hdr.ChunkID, 44, clc.wavefile); +} + +static char wavName[MAX_QPATH]; // compiler bug workaround +void CL_WriteWaveOpen() +{ + // we will just save it as a 16bit stereo 22050kz pcm file + + char name[MAX_OSPATH]; + int len; + char *s; + + if(Cmd_Argc() > 2) + { + Com_Printf("wav_record \n"); + return; + } + + if(clc.waverecording) + { + Com_Printf("Already recording a wav file\n"); + return; + } + + // yes ... no ? leave it up to them imo + //if (cl_avidemo.integer) + // return; + + if(Cmd_Argc() == 2) + { + s = Cmd_Argv(1); + Q_strncpyz(wavName, s, sizeof(wavName)); + Com_sprintf(name, sizeof(name), "wav/%s.wav", wavName); + } + else + { + int number; + + // I STOLE THIS + for(number = 0; number <= 9999; number++) + { + CL_WavFilename(number, wavName); + Com_sprintf(name, sizeof(name), "wav/%s.wav", wavName); + + len = FS_FileExists(name); + if(len <= 0) + { + break; // file doesn't exist + } + } + } + + Com_Printf("recording to %s.\n", name); + clc.wavefile = FS_FOpenFileWrite(name); + + if(!clc.wavefile) + { + Com_Printf("ERROR: couldn't open %s for writing.\n", name); + return; + } + + CL_WriteWaveHeader(); + clc.wavetime = -1; + + clc.waverecording = qtrue; + + Cvar_Set("cl_waverecording", "1"); + Cvar_Set("cl_wavefilename", wavName); + Cvar_Set("cl_waveoffset", "0"); +} + +void CL_WriteWaveClose() +{ + Com_Printf("Stopped recording\n"); + + hdr.Subchunk2Size = hdr.NumSamples * hdr.NumChannels * (hdr.BitsPerSample / 8); + hdr.ChunkSize = 36 + hdr.Subchunk2Size; + + FS_Seek(clc.wavefile, 4, FS_SEEK_SET); + FS_Write(&hdr.ChunkSize, 4, clc.wavefile); + FS_Seek(clc.wavefile, 40, FS_SEEK_SET); + FS_Write(&hdr.Subchunk2Size, 4, clc.wavefile); + + // and we're outta here + FS_FCloseFile(clc.wavefile); + clc.wavefile = 0; +} + +/* +==================== +CL_CompleteDemoName +==================== +*/ +static void CL_CompleteDemoName( char *args, int argNum ) +{ + if( argNum == 2 ) + { + char demoExt[ 16 ]; + + Com_sprintf( demoExt, sizeof( demoExt ), ".dm_%d", com_protocol->integer ); + Field_CompleteFilename( "demos", demoExt, qtrue ); + } +} + + + +/* +==================== +CL_PlayDemo_f + +demo + +==================== +*/ +void CL_PlayDemo_f(void) +{ + char name[MAX_OSPATH], extension[32]; + char *arg; + int prot_ver; + + if(Cmd_Argc() != 2) + { + Com_Printf("playdemo \n"); + return; + } + + // make sure a local server is killed + Cvar_Set("sv_killserver", "1"); + + CL_Disconnect(qtrue); + + +// CL_FlushMemory(); //----(SA) MEM NOTE: in missionpack, this is moved to CL_DownloadsComplete + + + // open the demo file + arg = Cmd_Argv(1); + prot_ver = com_protocol->integer - 1; + while(prot_ver <= com_protocol->integer && !clc.demofile) + { + Com_sprintf(extension, sizeof(extension), ".dm_%d", prot_ver); + if(!Q_stricmp(arg + strlen(arg) - strlen(extension), extension)) + { + Com_sprintf(name, sizeof(name), "demos/%s", arg); + } + else + { + Com_sprintf(name, sizeof(name), "demos/%s.dm_%d", arg, prot_ver); + } + FS_FOpenFileRead(name, &clc.demofile, qtrue); + prot_ver++; + } + 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; + + if(Cvar_VariableValue("cl_wavefilerecord")) + { + CL_WriteWaveOpen(); + } + + 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; +// if (clc.waverecording) { +// CL_WriteWaveClose(); +// clc.waverecording = qfalse; +// } +} + +/* +================== +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) +{ + + // clear sounds + S_DisableSounds(); + // download subsystem + DL_Shutdown(); + // shutdown CGame + CL_ShutdownCGame(); + // shutdown UI + CL_ShutdownUI(); + + // shutdown the renderer + if(re.Shutdown) + { + re.Shutdown(qfalse); // don't destroy window or context + } + + if(re.purgeCache) + { + CL_DoPurgeCache(); + } + + cls.uiStarted = qfalse; + cls.cgameStarted = qfalse; + cls.rendererStarted = qfalse; + cls.soundRegistered = qfalse; + + // Gordon: stop recording on map change etc, demos aren't valid over map changes anyway + if(clc.demorecording) + { + CL_StopRecord_f(); + } + + if(clc.waverecording) + { + CL_WavStopRecord_f(); + } +} + +/* +================= +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(); +} + +/* +===================== +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_cl_running->integer ) { + return; + } + + Con_Close(); + cls.keyCatchers = 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 + memset( cls.updateInfoString, 0, sizeof( cls.updateInfoString ) ); + memset( clc.serverMessage, 0, sizeof( clc.serverMessage ) ); + 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 + cls.keyCatchers = 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) +{ + Com_Memset(&cl, 0, sizeof(cl)); +} + +/* +===================== +CL_ClearStaticDownload +Clear download information that we keep in cls (disconnected download support) +===================== +*/ +void CL_ClearStaticDownload(void) +{ + assert(!cls.bWWWDlDisconnected); // reset before calling + cls.downloadRestart = qfalse; + cls.downloadTempName[0] = '\0'; + cls.downloadName[0] = '\0'; + cls.originalDownloadName[0] = '\0'; +} + +/* +===================== +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(!cls.bWWWDlDisconnected) + { + if(clc.download) + { + FS_FCloseFile(clc.download); + clc.download = 0; + } + *cls.downloadTempName = *cls.downloadName = 0; + Cvar_Set("cl_downloadName", ""); + + autoupdateStarted = qfalse; + autoupdateFilename[0] = '\0'; + } + +#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(); //----(SA) modified +#if 1 + // 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(); + } +#endif + CL_ClearState(); + + // wipe the client connection + Com_Memset(&clc, 0, sizeof(clc)); + + if(!cls.bWWWDlDisconnected) + { + CL_ClearStaticDownload(); + } + + // 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. + clc.voipEnabled = qfalse; +#endif + + // XreaL BEGIN + // stop recording any video + if(CL_VideoRecording()) + { + // finish rendering current frame + //SCR_UpdateScreen(); + CL_CloseAVI(); + } + // XreaL END + + // show_bug.cgi?id=589 + // don't try a restart if uivm is NULL, as we might be in the middle of a restart already + if(uivm && cls.state > CA_DISCONNECTED) + { + // restart the UI + cls.state = CA_DISCONNECTED; + + // shutdown the UI + CL_ShutdownUI(); + + // init the UI + CL_InitUI(); + } + else + { + cls.state = CA_DISCONNECTED; + } +} + + +/* +=================== +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\"\n", cmd); + return; + } + + if(Cmd_Argc() > 1) + { + CL_AddReliableCommand(string); + } + else + { + CL_AddReliableCommand(cmd); + } +} + + +#if defined (USE_HTTP) + +/* +=================== +CL_Highscore_response + +gets highscores results from the website +=================== +*/ +static int CL_Highscore_response( httpInfo_e code, const char * buffer, int length, void * notifyData ) +{ + if ( code == HTTP_WRITE ) { + Cvar_Set( "st_postresults", buffer ); + VM_Call(uivm, UI_REPORT_HIGHSCORE_RESPONSE ); + } + return 1; +} + +/* +=================== +CL_Highscore_f + +asks for highscores from the website +=================== +*/ +static void CL_Highscore_f( void ) { + + if ( Cmd_Argc() != 7 ) { + Com_Printf("Usage: highscore